diff --git a/Makefile b/Makefile index 71c65d9a..249e8b39 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,11 @@ pvtoolsSrc_DEPEND_DIRS = src src/ca DIRS += testApp testApp_DEPEND_DIRS = src + +DIRS += testCa +testCa_DEPEND_DIRS = src src/ca + + DIRS += examples examples_DEPEND_DIRS += src src/ca diff --git a/caProvider.md b/caProvider.md new file mode 100644 index 00000000..6a479aac --- /dev/null +++ b/caProvider.md @@ -0,0 +1,636 @@ +# pvAccessCPP: ca provider + +2018.07.09 + +Editors: + +* Marty Kraimer + +This is a description of channel provider **ca** that is implemented as part of **pvAccessCPP**. + +It uses the **channel access** network protocol to communicate with a server, +i. e. the network protocol that has been used to communicate with **EPICS IOCs** since 1990. + +Provider **pva** is another way to connect to a **DBRecord**, +But this only works if the IOC has **qsrv** installed. +**qsrv**, which is provided with +[pva2pva](https://github.com/epics-base/pva2pva), +has full support for communicating with a **DBRecord**. +The only advantage of **ca** is that it does require any changes to an existing IOC. + + +The following are discussed. + +* [Introduction](#S-introduction) +* [client API](#S-client) +* [Mapping DBD data to pvData](#S-dbd_to_pvdata) +* [Developing plugins for ca provider.](#S-plugin) + + + +## 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/src/ca/Makefile b/src/ca/Makefile index bc376f0c..93e6b27d 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -2,8 +2,7 @@ TOP = ../.. include $(TOP)/configure/CONFIG LIBRARY += pvAccessCA -pvAccessCA_LIBS += ca pvAccess pvData -LIB_LIBS += Com +pvAccessCA_LIBS += pvAccess pvData ca Com SHRLIB_VERSION ?= $(EPICS_PVA_MAJOR_VERSION).$(EPICS_PVA_MINOR_VERSION).$(EPICS_PVA_MAINTENANCE_VERSION) @@ -11,10 +10,12 @@ SHRLIB_VERSION ?= $(EPICS_PVA_MAJOR_VERSION).$(EPICS_PVA_MINOR_VERSION).$(EPICS_ LIB_SYS_LIBS_WIN32 += ws2_32 INC += pv/caProvider.h -INC += pv/caStatus.h +pvAccessCA_SRCS += monitorEventThread.cpp +pvAccessCA_SRCS += getDoneThread.cpp +pvAccessCA_SRCS += putDoneThread.cpp pvAccessCA_SRCS += caProvider.cpp pvAccessCA_SRCS += caChannel.cpp -pvAccessCA_SRCS += caStatus.cpp +pvAccessCA_SRCS += dbdToPv.cpp include $(TOP)/configure/RULES diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index a6c25411..b19349b1 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -10,11 +10,12 @@ #include #include #include -#include +#include "monitorEventThread.h" +#include "getDoneThread.h" +#include "putDoneThread.h" #define epicsExportSharedSymbols #include "caChannel.h" -#include using namespace epics::pvData; using std::string; @@ -39,10 +40,10 @@ CAChannel::shared_pointer CAChannel::create(CAChannelProvider::shared_pointer co if(DEBUG_LEVEL>0) { cout<< "CAChannel::create " << channelName << endl; } - CAChannelPtr thisPtr( + CAChannelPtr caChannel( new CAChannel(channelName, channelProvider, channelRequester)); - thisPtr->activate(priority); - return thisPtr; + caChannel->activate(priority); + return caChannel; } static void ca_connection_handler(struct connection_handler_args args) @@ -56,248 +57,11 @@ static void ca_connection_handler(struct connection_handler_args args) } } - -static ScalarType dbr2ST[] = -{ - pvString, // DBR_STRING = 0 - pvShort, // DBR_SHORT. DBR_INT = 1 - pvFloat, // DBR_FLOAT = 2 - static_cast(-1), // DBR_ENUM = 3 - pvByte, // DBR_CHAR = 4 - pvInt, // DBR_LONG = 5 - pvDouble // DBR_DOUBLE = 6 -}; - -static Structure::const_shared_pointer createStructure(CAChannel::shared_pointer const & channel, string const & properties) -{ - StandardFieldPtr standardField = getStandardField(); - Structure::const_shared_pointer structure; - - chtype channelType = channel->getNativeType(); - if (channelType != DBR_ENUM) - { - ScalarType st = dbr2ST[channelType]; - structure = (channel->getElementCount() > 1) ? - standardField->scalarArray(st, properties) : - standardField->scalar(st, properties); - } - else - { - // NOTE: enum arrays not supported - structure = standardField->enumerated(properties); - } - - return structure; -} - -static void ca_get_labels_handler(struct event_handler_args args) -{ - - if (args.status == ECA_NORMAL) - { - const dbr_gr_enum* dbr_enum_p = static_cast(args.dbr); - - PVStringArray* labelsArray = static_cast(args.usr); - if (labelsArray) - { - PVStringArray::svector labels(labelsArray->reuse()); - labels.resize(dbr_enum_p->no_str); - std::copy(dbr_enum_p->strs, dbr_enum_p->strs + dbr_enum_p->no_str, labels.begin()); - labelsArray->replace(freeze(labels)); - } - } - else - { - string mess("ca_get_labels_handler "); - mess += ca_message(args.status); - throw std::runtime_error(mess); - } -} - -// Filter out unrequested fields from a source structure according to a -// structure conforming to the format of the "field" field of a pvRequest, -// preserving type ids of unchanged structures. If ntTop is true also preserve -// type id if none of the deleted top-level subfields are the value field. -static StructureConstPtr refineStructure(StructureConstPtr const & source, - StructureConstPtr const & requestedFields, bool ntTop) -{ - if (requestedFields.get() == NULL || requestedFields->getNumberFields() == 0) - return source; - - FieldBuilderPtr builder = getFieldCreate()->createFieldBuilder(); - bool addId = true; - - FieldConstPtrArray fields = source->getFields(); - StringArray names = source->getFieldNames(); - size_t i = 0; - for (FieldConstPtrArray::const_iterator it = fields.begin(); it != fields.end(); ++it) - { - FieldConstPtr field = *it; - const std::string & name = names[i++]; - FieldConstPtr reqField = requestedFields->getField(name); - if (reqField.get()) - { - if (field->getType() != structure || (reqField->getType() != structure)) - builder->add(name,field); - else - { - StructureConstPtr substruct = - std::tr1::dynamic_pointer_cast(field); - - StructureConstPtr reqSubstruct = - std::tr1::dynamic_pointer_cast(reqField); - - StructureConstPtr nested = refineStructure(substruct, reqSubstruct, false); - builder->add(name,nested); - if (nested->getID() != substruct->getID()) - addId = false; - } - } - else if (!ntTop || name == "value") - addId = false; - } - if (addId) - builder->setId(source->getID()); - return builder->createStructure(); -} - -static PVStructure::shared_pointer createPVStructure(CAChannel::shared_pointer const & channel, string const & properties, PVStructurePtr pvRequest) -{ - StructureConstPtr unrefinedStructure = createStructure(channel, properties); - - PVStructurePtr fieldPVStructure = pvRequest->getSubField("field"); - StructureConstPtr finalStructure = fieldPVStructure.get() ? - refineStructure(unrefinedStructure, fieldPVStructure->getStructure(),true) : - unrefinedStructure; - - PVStructure::shared_pointer pvStructure = getPVDataCreate()->createPVStructure(finalStructure); - if (channel->getNativeType() == DBR_ENUM) - { - PVScalarArrayPtr pvScalarArray = pvStructure->getSubField("value.choices"); - - // TODO avoid getting labels if DBR_GR_ENUM or DBR_CTRL_ENUM is used in subsequent get - int result = ca_array_get_callback( - DBR_GR_ENUM, 1, channel->getChannelID(), ca_get_labels_handler, pvScalarArray.get()); - if (result == ECA_NORMAL) - { - result = ca_flush_io(); - // NOTE: we do not wait here, since all subsequent request (over TCP) is serialized - // and will guarantee that ca_get_labels_handler is called first - } - if (result != ECA_NORMAL){ - string mess(channel->getChannelName() + "PVStructure::shared_pointer createPVStructure "); - mess += "failed to get labels for enum "; - throw std::runtime_error(mess); - } - } - return pvStructure; -} - -static PVStructure::shared_pointer createPVStructure(CAChannel::shared_pointer const & channel, chtype dbrType, PVStructurePtr pvRequest) -{ - // Match to closest DBR type - // NOTE: value is always there - string properties; - bool isArray = channel->getElementCount() > 1; - if (dbrType >= DBR_CTRL_STRING) // 28 - { - if (dbrType != DBR_CTRL_STRING && dbrType != DBR_CTRL_ENUM) - { - if (isArray) - properties = "value,alarm,display"; - else - properties = "value,alarm,display,valueAlarm,control"; - } - else - properties = "value,alarm"; - } - else if (dbrType >= DBR_GR_STRING) // 21 - { - if (dbrType != DBR_GR_STRING && dbrType != DBR_GR_ENUM) - { - if (isArray) - properties = "value,alarm,display"; - else - properties = "value,alarm,display,valueAlarm"; - } - else - properties = "value,alarm"; - } - else if (dbrType >= DBR_TIME_STRING) // 14 - properties = "value,alarm,timeStamp"; - else if (dbrType >= DBR_STS_STRING) // 7 - properties = "value,alarm"; - else - properties = "value"; - - return createPVStructure(channel, properties, pvRequest); -} - - void CAChannel::connected() { if(DEBUG_LEVEL>0) { cout<< "CAChannel::connected " << channelName << endl; } - std::queue putQ; - std::queue getQ; - std::queue monitorQ; - { - Lock lock(requestsMutex); - // we assume array if element count > 1 - elementCount = ca_element_count(channelID); - channelType = ca_field_type(channelID); - bool isArray = elementCount > 1; - - // no valueAlarm and control,display for non-numeric type - // no control,display for numeric arrays - string allProperties = - (channelType != DBR_STRING && channelType != DBR_ENUM) ? - isArray ? - "value,timeStamp,alarm,display" : - "value,timeStamp,alarm,display,valueAlarm,control" : - "value,timeStamp,alarm"; - Structure::const_shared_pointer structure = createStructure( - shared_from_this(), allProperties); - - // TODO we need only Structure here - this->structure = structure; - - std::vector::const_iterator getiter; - for (getiter = getList.begin(); getiter != getList.end(); ++getiter) { - CAChannelGetPtr temp = (*getiter).lock(); - if(!temp) continue; - getQ.push(temp); - } - std::vector::const_iterator putiter; - for (putiter = putList.begin(); putiter != putList.end(); ++putiter) { - CAChannelPutPtr temp = (*putiter).lock(); - if(!temp) continue; - putQ.push(temp); - } - std::vector::const_iterator monitoriter; - for (monitoriter = monitorList.begin(); monitoriter != monitorList.end(); ++monitoriter) { - CAChannelMonitorPtr temp = (*monitoriter).lock(); - if(!temp) continue; - monitorQ.push(temp); - } - } - while(!putQ.empty()) { - putQ.front()->channelCreated(Status::Ok,shared_from_this()); - putQ.pop(); - } - while(!getQ.empty()) { - getQ.front()->channelCreated(Status::Ok,shared_from_this()); - getQ.pop(); - } - while(!monitorQ.empty()) { - monitorQ.front()->channelCreated(Status::Ok,shared_from_this()); - monitorQ.pop(); - } - while(!getFieldQueue.empty()) { - getFieldQueue.front()->callRequester(shared_from_this()); - getFieldQueue.pop(); - } while(!putQueue.empty()) { putQueue.front()->activate(); putQueue.pop(); @@ -307,7 +71,9 @@ void CAChannel::connected() getQueue.pop(); } while(!monitorQueue.empty()) { - monitorQueue.front()->activate(); + CAChannelMonitorPtr monitor(monitorQueue.front()); + monitor->activate(); + addMonitor(monitor); monitorQueue.pop(); } ChannelRequester::shared_pointer req(channelRequester.lock()); @@ -322,42 +88,7 @@ void CAChannel::disconnected() if(DEBUG_LEVEL>0) { cout<< "CAChannel::disconnected " << channelName << endl; } - std::queue putQ; - std::queue getQ; - std::queue monitorQ; - { - Lock lock(requestsMutex); - std::vector::const_iterator getiter; - for (getiter = getList.begin(); getiter != getList.end(); ++getiter) { - CAChannelGetPtr temp = (*getiter).lock(); - if(!temp) continue; - getQ.push(temp); - } - std::vector::const_iterator putiter; - for (putiter = putList.begin(); putiter != putList.end(); ++putiter) { - CAChannelPutPtr temp = (*putiter).lock(); - if(!temp) continue; - putQ.push(temp); - } - std::vector::const_iterator monitoriter; - for (monitoriter = monitorList.begin(); monitoriter != monitorList.end(); ++monitoriter) { - CAChannelMonitorPtr temp = (*monitoriter).lock(); - if(!temp) continue; - monitorQ.push(temp); - } - } - while(!putQ.empty()) { - putQ.front()->channelDisconnect(false); - putQ.pop(); - } - while(!getQ.empty()) { - getQ.front()->channelDisconnect(false); - getQ.pop(); - } - while(!monitorQ.empty()) { - monitorQ.front()->channelDisconnect(false); - monitorQ.pop(); - } + ChannelRequester::shared_pointer req(channelRequester.lock()); if(req) { EXCEPTION_GUARD(req->channelStateChange( @@ -365,17 +96,13 @@ void CAChannel::disconnected() } } -size_t CAChannel::num_instances; - -CAChannel::CAChannel(std::string const & _channelName, - CAChannelProvider::shared_pointer const & _channelProvider, - ChannelRequester::shared_pointer const & _channelRequester) : - channelName(_channelName), - channelProvider(_channelProvider), - channelRequester(_channelRequester), +CAChannel::CAChannel(std::string const & channelName, + CAChannelProvider::shared_pointer const & channelProvider, + ChannelRequester::shared_pointer const & channelRequester) : + channelName(channelName), + channelProvider(channelProvider), + channelRequester(channelRequester), channelID(0), - channelType(0), - elementCount(0), channelCreated(false) { if(DEBUG_LEVEL>0) { @@ -411,7 +138,13 @@ void CAChannel::activate(short priority) CAChannel::~CAChannel() { if(DEBUG_LEVEL>0) { - cout << "CAChannel::~CAChannel() " << channelName << endl; + cout << "CAChannel::~CAChannel() " << channelName + << " channelCreated " << (channelCreated ? "true" : "false") + << endl; + } + { + Lock lock(requestsMutex); + if(!channelCreated) return; } disconnectChannel(); } @@ -429,8 +162,19 @@ void CAChannel::disconnectChannel() if(!channelCreated) return; channelCreated = false; } + std::vector::iterator it; + for(it = monitorlist.begin(); it!=monitorlist.end(); ++it) + { + CAChannelMonitorPtr mon = (*it).lock(); + if(!mon) continue; + mon->stop(); + } + monitorlist.resize(0); /* Clear CA Channel */ - attachContext(); + CAChannelProviderPtr provider(channelProvider.lock()); + if(provider) { + std::tr1::static_pointer_cast(provider)->attachContext(); + } int result = ca_clear_channel(channelID); if (result == ECA_NORMAL) return; string mess("CAChannel::disconnectChannel() "); @@ -438,77 +182,11 @@ void CAChannel::disconnectChannel() cerr << mess << endl; } - - -void CAChannel::addChannelGet(const CAChannelGetPtr & get) -{ - if(DEBUG_LEVEL>0) { - cout<< "CAChannel::addChannelGet " << channelName << endl; - } - Lock lock(requestsMutex); - for(size_t i=0; i< getList.size(); ++i) { - if(!(getList[i].lock())) { - getList[i] = get; - return; - } - } - getList.push_back(get); -} - -void CAChannel::addChannelPut(const CAChannelPutPtr & put) -{ - if(DEBUG_LEVEL>0) { - cout<< "CAChannel::addChannelPut " << channelName << endl; - } - Lock lock(requestsMutex); - for(size_t i=0; i< putList.size(); ++i) { - if(!(putList[i].lock())) { - putList[i] = put; - return; - } - } - putList.push_back(put); -} - - -void CAChannel::addChannelMonitor(const CAChannelMonitorPtr & monitor) -{ - if(DEBUG_LEVEL>0) { - cout<< "CAChannel::addChannelMonitor " << channelName << endl; - } - Lock lock(requestsMutex); - for(size_t i=0; i< monitorList.size(); ++i) { - if(!(monitorList[i].lock())) { - monitorList[i] = monitor; - return; - } - } - monitorList.push_back(monitor); -} - chid CAChannel::getChannelID() { return channelID; } - -chtype CAChannel::getNativeType() -{ - return channelType; -} - - -unsigned CAChannel::getElementCount() -{ - return elementCount; -} - -Structure::const_shared_pointer CAChannel::getStructure() -{ - return structure; -} - - std::tr1::shared_ptr CAChannel::getProvider() { return channelProvider.lock(); @@ -564,7 +242,7 @@ void CAChannel::getField(GetFieldRequester::shared_pointer const & requester, } -AccessRights CAChannel::getAccessRights(epics::pvData::PVField::shared_pointer const & /*pvField*/) +AccessRights CAChannel::getAccessRights(PVField::shared_pointer const & /*pvField*/) { if (ca_write_access(channelID)) return readWrite; @@ -577,7 +255,7 @@ AccessRights CAChannel::getAccessRights(epics::pvData::PVField::shared_pointer c ChannelGet::shared_pointer CAChannel::createChannelGet( ChannelGetRequester::shared_pointer const & channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) { if(DEBUG_LEVEL>0) { cout << "CAChannel::createChannelGet " << channelName << endl; @@ -598,7 +276,7 @@ ChannelGet::shared_pointer CAChannel::createChannelGet( ChannelPut::shared_pointer CAChannel::createChannelPut( ChannelPutRequester::shared_pointer const & channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) { if(DEBUG_LEVEL>0) { cout << "CAChannel::createChannelPut " << channelName << endl; @@ -619,7 +297,7 @@ ChannelPut::shared_pointer CAChannel::createChannelPut( Monitor::shared_pointer CAChannel::createMonitor( MonitorRequester::shared_pointer const & monitorRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) { if(DEBUG_LEVEL>0) { cout << "CAChannel::createMonitor " << channelName << endl; @@ -634,9 +312,22 @@ Monitor::shared_pointer CAChannel::createMonitor( } } channelMonitor->activate(); + addMonitor(channelMonitor); return channelMonitor; } +void CAChannel::addMonitor(CAChannelMonitorPtr const & monitor) +{ + std::vector::iterator it; + for(it = monitorlist.begin(); it!=monitorlist.end(); ++it) + { + CAChannelMonitorWPtr mon = *it; + if(mon.lock()) continue; + mon = monitor; + return; + } + monitorlist.push_back(monitor); +} void CAChannel::printInfo(std::ostream& out) { @@ -676,7 +367,10 @@ void CAChannelGetField::callRequester(CAChannelPtr const & caChannel) } GetFieldRequester::shared_pointer requester(getFieldRequester.lock()); if(!requester) return; - epics::pvData::Structure::const_shared_pointer structure(caChannel->getStructure()); + PVStructurePtr pvRequest(createRequest("")); + DbdToPvPtr dbdToPv = DbdToPv::create(caChannel,pvRequest,getIO); + PVStructurePtr pvStructure = dbdToPv->createPVStructure(); + Structure::const_shared_pointer structure(pvStructure->getStructure()); Field::const_shared_pointer field = subField.empty() ? std::tr1::static_pointer_cast(structure) : @@ -700,67 +394,34 @@ void CAChannel::attachContext() CAChannelProviderPtr provider(channelProvider.lock()); if(provider) { std::tr1::static_pointer_cast(provider)->attachContext(); + return; } + string mess("CAChannel::attachContext provider does not exist "); + mess += getChannelName(); + throw std::runtime_error(mess); } -static chtype getDBRType(PVStructure::shared_pointer const & pvRequest, chtype nativeType) -{ - // get "field" sub-structure - PVStructure::shared_pointer fieldSubField = - std::tr1::dynamic_pointer_cast(pvRequest->getSubField("field")); - if (!fieldSubField) - fieldSubField = pvRequest; - Structure::const_shared_pointer fieldStructure = fieldSubField->getStructure(); - - // no fields - if (fieldStructure->getNumberFields() == 0) - { - return static_cast(static_cast(nativeType) + DBR_TIME_STRING); - } - // control -> DBR_CTRL_ - if (fieldStructure->getField("control")) - return static_cast(static_cast(nativeType) + DBR_CTRL_STRING); - - // display/valueAlarm -> DBR_GR_ - if (fieldStructure->getField("display") || fieldStructure->getField("valueAlarm")) - return static_cast(static_cast(nativeType) + DBR_GR_STRING); - - // timeStamp -> DBR_TIME_ - // NOTE: that only DBR_TIME_ type holds timestamp, therefore if you request for - // the fields above, you will never get timestamp - if (fieldStructure->getField("timeStamp")) - return static_cast(static_cast(nativeType) + DBR_TIME_STRING); - - // alarm -> DBR_STS_ - if (fieldStructure->getField("alarm")) - return static_cast(static_cast(nativeType) + DBR_STS_STRING); - - return nativeType; -} - -size_t CAChannelGet::num_instances; - CAChannelGetPtr CAChannelGet::create( CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) { + if(DEBUG_LEVEL>0) { + cout << "CAChannelGet::create " << channel->getChannelName() << endl; + } return CAChannelGetPtr(new CAChannelGet(channel, channelGetRequester, pvRequest)); } - CAChannelGet::CAChannelGet(CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) : channel(channel), channelGetRequester(channelGetRequester), - pvRequest(pvRequest) -{ - if(DEBUG_LEVEL>0) { - cout << "CAChannelGet::CAChannelGet() " << channel->getChannelName() << endl; - } -} + pvRequest(pvRequest), + getStatus(Status::Ok), + getDoneThread(GetDoneThread::get()) +{} CAChannelGet::~CAChannelGet() { @@ -772,65 +433,22 @@ CAChannelGet::~CAChannelGet() void CAChannelGet::activate() { ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelGet::activate " << channel->getChannelName() << " requester "<getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); - bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); - bitSet->set(0); - channel->addChannelGet(shared_from_this()); - if(channel->getConnectionState()==Channel::CONNECTED) { - EXCEPTION_GUARD(getRequester->channelGetConnect(Status::Ok, shared_from_this(), - pvStructure->getStructure())); - } -} - -void CAChannelGet::channelCreated(const Status& status,Channel::shared_pointer const & cl) -{ if(DEBUG_LEVEL>0) { - std::cout << "CAChannelGet::channelCreated " << channel->getChannelName() << endl; - } - ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); - if(!getRequester) return; - chtype newType = getDBRType(pvRequest, channel->getNativeType()); - if(newType!=getType) { - getType = getDBRType(pvRequest, channel->getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); - bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); - bitSet->set(0); + std::cout << "CAChannelGet::activate " << channel->getChannelName() << endl; } + dbdToPv = DbdToPv::create(channel,pvRequest,getIO); + pvStructure = dbdToPv->createPVStructure(); + bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); + notifyGetRequester = NotifyGetRequesterPtr(new NotifyGetRequester()); + notifyGetRequester->setChannelGet(shared_from_this()); EXCEPTION_GUARD(getRequester->channelGetConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } -void CAChannelGet::channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState) -{ - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelGet::channelStateChange " << channel->getChannelName() << endl; - } - ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); - if(!getRequester) return; - if(connectionState==Channel::DISCONNECTED || connectionState==Channel::DESTROYED) { - EXCEPTION_GUARD(getRequester->channelDisconnect(connectionState==Channel::DESTROYED);) - } -} -void CAChannelGet::channelDisconnect(bool destroy) -{ - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelGet::channelDisconnect " << channel->getChannelName() << endl; - } - ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); - if(!getRequester) return; - EXCEPTION_GUARD(getRequester->channelDisconnect(destroy);) -} -/* --------------- epics::pvAccess::ChannelGet --------------- */ +std::string CAChannelGet::getRequesterName() { return "CAChannelGet";} namespace { @@ -840,536 +458,122 @@ static void ca_get_handler(struct event_handler_args args) channelGet->getDone(args); } -typedef void (*copyDBRtoPVStructure)(const void * from, unsigned count, PVStructure::shared_pointer const & to); - +} // namespace -// template -template -void copy_DBR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) +void CAChannelGet::getDone(struct event_handler_args &args) { - if (count == 1) - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) value->put(static_cast(dbr)[0]); - } - else - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - typename aF::svector temp(value->reuse()); - temp.resize(count); - std::copy(static_cast(dbr), static_cast(dbr) + count, temp.begin()); - value->replace(freeze(temp)); - } + if(DEBUG_LEVEL>1) { + std::cout << "CAChannelGet::getDone " + << channel->getChannelName() << endl; } + + ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); + if(!getRequester) return; + getStatus = dbdToPv->getFromDBD(pvStructure,bitSet,args); + getDoneThread->getDone(notifyGetRequester); } -#if defined(__vxworks) || defined(__rtems__) -// dbr_long_t is defined as "int", pvData uses int32 which can be defined as "long int" (32-bit) -// template -template<> -void copy_DBR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) +void CAChannelGet::notifyClient() { - if (count == 1) - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) value->put(static_cast(dbr)[0]); - } - else - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) - { - PVIntArray::svector temp(value->reuse()); - temp.resize(count); - std::copy(static_cast(dbr), static_cast(dbr) + count, temp.begin()); - value->replace(freeze(temp)); - } - } + ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); + if(!getRequester) return; + EXCEPTION_GUARD(getRequester->getDone(getStatus, shared_from_this(), pvStructure, bitSet)); } -#endif -// string specialization -template<> -void copy_DBR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) +void CAChannelGet::get() { - if (count == 1) + if(DEBUG_LEVEL>1) { + std::cout << "CAChannelGet::get " << channel->getChannelName() << endl; + } + ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); + if(!getRequester) return; + channel->attachContext(); + bitSet->clear(); + int result = ca_array_get_callback(dbdToPv->getRequestType(), + 0, + channel->getChannelID(), ca_get_handler, this); + if (result == ECA_NORMAL) { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) value->put(std::string(static_cast(dbr))); + result = ca_flush_io(); } - else + if (result != ECA_NORMAL) { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) - { - const dbr_string_t* dbrStrings = static_cast(dbr); - PVStringArray::svector sA(value->reuse()); - sA.resize(count); - std::copy(dbrStrings, dbrStrings + count, sA.begin()); - value->replace(freeze(sA)); - } + string mess("CAChannelGet::get "); + mess += channel->getChannelName() + " message " + ca_message(result); + getStatus = Status(Status::STATUSTYPE_ERROR,mess); + notifyClient(); } } -// enum specialization -template<> -void copy_DBR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) +Channel::shared_pointer CAChannelGet::getChannel() { - if (count == 1) - { - PVIntPtr value = pvStructure->getSubField("value.index"); - if (value.get()) value->put(static_cast(dbr)[0]); - } - else - { - // not supported - std::cerr << "caChannel: array of enums not supported" << std::endl; - } + return channel; } -// template -template -void copy_DBR_STS(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) +void CAChannelGet::cancel() { - const T* data = static_cast(dbr); - - PVStructure::shared_pointer alarm = pvStructure->getSubField("alarm"); - if (alarm.get()) - { - PVIntPtr status = alarm->getSubField("status"); - if (status.get()) status->put(dbrStatus2alarmStatus[data->status]); - - PVIntPtr severity = alarm->getSubField("severity"); - if (severity.get()) severity->put(data->severity); - - PVStringPtr message = alarm->getSubField("message"); - if (message.get()) message->put(dbrStatus2alarmMessage[data->status]); - } - - copy_DBR(&data->value, count, pvStructure); } -// template -template -void copy_DBR_TIME(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) +void CAChannelGet::lastRequest() { - const T* data = static_cast(dbr); - - PVStructure::shared_pointer ts = pvStructure->getSubField("timeStamp"); - if (ts.get()) - { - epics::pvData::int64 spe = data->stamp.secPastEpoch; - spe += 7305*86400; - - PVLongPtr secondsPastEpoch = ts->getSubField("secondsPastEpoch"); - if (secondsPastEpoch.get()) secondsPastEpoch->put(spe); +} - PVIntPtr nanoseconds = ts->getSubField("nanoseconds"); - if (nanoseconds.get()) nanoseconds->put(data->stamp.nsec); +CAChannelPutPtr CAChannelPut::create( + CAChannel::shared_pointer const & channel, + ChannelPutRequester::shared_pointer const & channelPutRequester, + PVStructure::shared_pointer const & pvRequest) +{ + if(DEBUG_LEVEL>0) { + cout << "CAChannelPut::create " << channel->getChannelName() << endl; } - - copy_DBR_STS(dbr, count, pvStructure); + return CAChannelPutPtr(new CAChannelPut(channel, channelPutRequester, pvRequest)); } +CAChannelPut::CAChannelPut(CAChannel::shared_pointer const & channel, + ChannelPutRequester::shared_pointer const & channelPutRequester, + PVStructure::shared_pointer const & pvRequest) +: + channel(channel), + channelPutRequester(channelPutRequester), + pvRequest(pvRequest), + block(false), + isPut(false), + getStatus(Status::Ok), + putStatus(Status::Ok), + putDoneThread(PutDoneThread::get()) +{} -template -void copy_format(const void * /*dbr*/, PVStructure::shared_pointer const & pvDisplayStructure) +CAChannelPut::~CAChannelPut() { - PVStringPtr format = pvDisplayStructure->getSubField("format"); - if (format.get()) format->put("%d"); -} - -#define COPY_FORMAT_FOR(T) \ -template <> \ -void copy_format(const void * dbr, PVStructure::shared_pointer const & pvDisplayStructure) \ -{ \ - const T* data = static_cast(dbr); \ -\ - if (data->precision) \ - { \ - char fmt[16]; \ - sprintf(fmt, "%%.%df", data->precision); \ - PVStringPtr format = pvDisplayStructure->getSubField("format");\ - if (format.get()) format->put(std::string(fmt));\ - } \ - else \ - { \ - PVStringPtr format = pvDisplayStructure->getSubField("format");\ - if (format.get()) format->put("%f");\ - } \ + if(DEBUG_LEVEL>0) { + std::cout << "CAChannelPut::~CAChannelPut() " << channel->getChannelName() << endl; + } } -COPY_FORMAT_FOR(dbr_gr_float) -COPY_FORMAT_FOR(dbr_ctrl_float) -COPY_FORMAT_FOR(dbr_gr_double) -COPY_FORMAT_FOR(dbr_ctrl_double) -#undef COPY_FORMAT_FOR - -// template -template -void copy_DBR_GR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) +void CAChannelPut::activate() { - const T* data = static_cast(dbr); - - PVStructurePtr alarm = pvStructure->getSubField("alarm"); - if (alarm.get()) - { - PVIntPtr status = alarm->getSubField("status"); - if (status.get()) status->put(dbrStatus2alarmStatus[data->status]); - - PVIntPtr severity = alarm->getSubField("severity"); - if (severity.get()) severity->put(data->severity); - - PVStringPtr message = alarm->getSubField("message"); - if (message.get()) message->put(dbrStatus2alarmMessage[data->status]); + ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); + if(!putRequester) return; + if(DEBUG_LEVEL>0) { + cout << "CAChannelPut::activate " << channel->getChannelName() << endl; } - - PVStructurePtr disp = pvStructure->getSubField("display"); - if (disp.get()) - { - PVStringPtr units = disp->getSubField("units"); - if (units.get()) units->put(std::string(data->units)); - - PVDoublePtr limitHigh = disp->getSubField("limitHigh"); - if (limitHigh.get()) limitHigh->put(data->upper_disp_limit); - - PVDoublePtr limitLow = disp->getSubField("limitLow"); - if (limitLow.get()) limitLow->put(data->lower_disp_limit); - - copy_format(dbr, disp); - } - - PVStructurePtr va = pvStructure->getSubField("valueAlarm"); - if (va.get()) - { - std::tr1::shared_ptr highAlarmLimit = va->getSubField("highAlarmLimit"); - if (highAlarmLimit.get()) highAlarmLimit->put(data->upper_alarm_limit); - - std::tr1::shared_ptr highWarningLimit = va->getSubField("highWarningLimit"); - if (highWarningLimit.get()) highWarningLimit->put(data->upper_warning_limit); - - std::tr1::shared_ptr lowWarningLimit = va->getSubField("lowWarningLimit"); - if (lowWarningLimit.get()) lowWarningLimit->put(data->lower_warning_limit); - - std::tr1::shared_ptr lowAlarmLimit = va->getSubField("lowAlarmLimit"); - if (lowAlarmLimit.get()) lowAlarmLimit->put(data->lower_alarm_limit); - } - - copy_DBR(&data->value, count, pvStructure); -} - -// enum specialization -template<> -void copy_DBR_GR -(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - const dbr_gr_enum* data = static_cast(dbr); - - copy_DBR_STS(data, count, pvStructure); -} - - -// template -template -void copy_DBR_CTRL(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - const T* data = static_cast(dbr); - - PVStructure::shared_pointer alarm = pvStructure->getSubField("alarm"); - if (alarm.get()) - { - PVIntPtr status = alarm->getSubField("status"); - if (status.get()) status->put(dbrStatus2alarmStatus[data->status]); - - PVIntPtr severity = alarm->getSubField("severity"); - if (severity.get()) severity->put(data->severity); - - PVStringPtr message = alarm->getSubField("message"); - if (message.get()) message->put(dbrStatus2alarmMessage[data->status]); - } - - PVStructurePtr disp = pvStructure->getSubField("display"); - if (disp.get()) - { - PVStringPtr units = disp->getSubField("units"); - if (units.get()) units->put(std::string(data->units)); - - PVDoublePtr limitHigh = disp->getSubField("limitHigh"); - if (limitHigh.get()) limitHigh->put(data->upper_disp_limit); - - PVDoublePtr limitLow = disp->getSubField("limitLow"); - if (limitLow.get()) limitLow->put(data->lower_disp_limit); - - copy_format(dbr, disp); - } - - PVStructurePtr va = pvStructure->getSubField("valueAlarm"); - if (va.get()) - { - std::tr1::shared_ptr highAlarmLimit = va->getSubField("highAlarmLimit"); - if (highAlarmLimit.get()) highAlarmLimit->put(data->upper_alarm_limit); - - std::tr1::shared_ptr highWarningLimit = va->getSubField("highWarningLimit"); - if (highWarningLimit.get()) highWarningLimit->put(data->upper_warning_limit); - - std::tr1::shared_ptr lowWarningLimit = va->getSubField("lowWarningLimit"); - if (lowWarningLimit.get()) lowWarningLimit->put(data->lower_warning_limit); - - std::tr1::shared_ptr lowAlarmLimit = va->getSubField("lowAlarmLimit"); - if (lowAlarmLimit.get()) lowAlarmLimit->put(data->lower_alarm_limit); - } - - PVStructurePtr ctrl = pvStructure->getSubField("control"); - if (ctrl.get()) - { - PVDoublePtr limitHigh = ctrl->getSubField("limitHigh"); - if (limitHigh.get()) limitHigh->put(data->upper_ctrl_limit); - - PVDoublePtr limitLow = ctrl->getSubField("limitLow"); - if (limitLow.get()) limitLow->put(data->lower_ctrl_limit); - } - - copy_DBR(&data->value, count, pvStructure); -} - -// enum specialization -template<> -void copy_DBR_CTRL -(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - const dbr_ctrl_enum* data = static_cast(dbr); - - copy_DBR_STS(data, count, pvStructure); -} - - -static copyDBRtoPVStructure copyFuncTable[] = -{ - copy_DBR, // DBR_STRING - copy_DBR, // DBR_INT, DBR_SHORT - copy_DBR, // DBR_FLOAT - copy_DBR, // DBR_ENUM - copy_DBR, // DBR_CHAR - copy_DBR, // DBR_LONG - copy_DBR, // DBR_DOUBLE - - copy_DBR_STS, // DBR_STS_STRING - copy_DBR_STS, // DBR_STS_INT, DBR_STS_SHORT - copy_DBR_STS, // DBR_STS_FLOAT - copy_DBR_STS, // DBR_STS_ENUM - copy_DBR_STS, // DBR_STS_CHAR - copy_DBR_STS, // DBR_STS_LONG - copy_DBR_STS, // DBR_STS_DOUBLE - - copy_DBR_TIME, // DBR_TIME_STRING - copy_DBR_TIME, // DBR_TIME_INT, DBR_TIME_SHORT - copy_DBR_TIME, // DBR_TIME_FLOAT - copy_DBR_TIME, // DBR_TIME_ENUM - copy_DBR_TIME, // DBR_TIME_CHAR - copy_DBR_TIME, // DBR_TIME_LONG - copy_DBR_TIME, // DBR_TIME_DOUBLE - - copy_DBR_STS, // DBR_GR_STRING -> DBR_STS_STRING - copy_DBR_GR, // DBR_GR_INT, DBR_GR_SHORT - copy_DBR_GR, // DBR_GR_FLOAT - copy_DBR_GR, // DBR_GR_ENUM - copy_DBR_GR, // DBR_GR_CHAR - copy_DBR_GR, // DBR_GR_LONG - copy_DBR_GR, // DBR_GR_DOUBLE - - copy_DBR_STS, // DBR_CTRL_STRING -> DBR_STS_STRING - copy_DBR_CTRL, // DBR_CTRL_INT, DBR_CTRL_SHORT - copy_DBR_CTRL, // DBR_CTRL_FLOAT - copy_DBR_CTRL, // DBR_CTRL_ENUM - copy_DBR_CTRL, // DBR_CTRL_CHAR - copy_DBR_CTRL, // DBR_CTRL_LONG - copy_DBR_CTRL // DBR_CTRL_DOUBLE -}; - -} // namespace - -void CAChannelGet::getDone(struct event_handler_args &args) -{ - if(DEBUG_LEVEL>1) { - std::cout << "CAChannelGet::getDone " << channel->getChannelName() << endl; - } - ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); - if(!getRequester) return; - if (args.status == ECA_NORMAL) - { - copyDBRtoPVStructure copyFunc = copyFuncTable[getType]; - if (copyFunc) - copyFunc(args.dbr, args.count, pvStructure); - else - { - throw std::runtime_error("CAChannelGet::getDone no copy func implemented"); - } - EXCEPTION_GUARD(getRequester->getDone(Status::Ok, shared_from_this(), pvStructure, bitSet)); - } - else - { - Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); - EXCEPTION_GUARD(getRequester->getDone(errorStatus, shared_from_this(), - PVStructure::shared_pointer(), BitSet::shared_pointer())); - } -} - - -void CAChannelGet::get() -{ - if(DEBUG_LEVEL>1) { - std::cout << "CAChannelGet::get " << channel->getChannelName() << endl; - } - ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); - if(!getRequester) return; - channel->attachContext(); - - /* - From R3.14.12 onwards ca_array_get_callback() replies will give a CA client application the current number - of elements in an array field, provided they specified an element count of zero in their original request. - The element count is passed in the callback argument structure. - Prior to R3.14.12 requesting zero elements in a ca_array_get_callback() call was illegal and would fail - immediately. - */ - - int result = ca_array_get_callback(getType, - 0, - channel->getChannelID(), ca_get_handler, this); - if (result == ECA_NORMAL) - { - result = ca_flush_io(); - } - if (result == ECA_NORMAL) return; - string mess("CAChannelGet::get "); - mess += ca_message(result); - throw std::runtime_error(mess); -} - - -/* --------------- epics::pvData::ChannelRequest --------------- */ - -Channel::shared_pointer CAChannelGet::getChannel() -{ - return channel; -} - -void CAChannelGet::cancel() -{ -} - -void CAChannelGet::lastRequest() -{ -} - - - -CAChannelPutPtr CAChannelPut::create( - CAChannel::shared_pointer const & channel, - ChannelPutRequester::shared_pointer const & channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) -{ - return CAChannelPutPtr(new CAChannelPut(channel, channelPutRequester, pvRequest)); -} - - -CAChannelPut::~CAChannelPut() -{ - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelPut::~CAChannelPut() " << channel->getChannelName() << endl; - } -} - -size_t CAChannelPut::num_instances; - -CAChannelPut::CAChannelPut(CAChannel::shared_pointer const & channel, - ChannelPutRequester::shared_pointer const & channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) -: - channel(channel), - channelPutRequester(channelPutRequester), - pvRequest(pvRequest), - block(false) -{ - if(DEBUG_LEVEL>0) { - cout << "CAChannelPut::CAChannePut() " << channel->getChannelName() << endl; - } -} - -void CAChannelPut::activate() -{ - if(DEBUG_LEVEL>0) { - cout << "CAChannelPut::activate " << channel->getChannelName() << endl; - } - ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); - if(!putRequester) return; - if(pvStructure) throw std::runtime_error("CAChannelPut::activate() was called twice"); - getType = getDBRType(pvRequest,channel->getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); + dbdToPv = DbdToPv::create(channel,pvRequest,putIO); + pvStructure = dbdToPv->createPVStructure(); bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); PVStringPtr pvString = pvRequest->getSubField("record._options.block"); if(pvString) { std::string val = pvString->get(); if(val.compare("true")==0) block = true; } - bitSet->set(pvStructure->getSubFieldT("value")->getFieldOffset()); - channel->addChannelPut(shared_from_this()); - if(channel->getConnectionState()==Channel::CONNECTED) { - EXCEPTION_GUARD(putRequester->channelPutConnect(Status::Ok, shared_from_this(), - pvStructure->getStructure())); - } -} - - -void CAChannelPut::channelCreated(const Status& status,Channel::shared_pointer const & c) -{ - if(DEBUG_LEVEL>0) { - cout << "CAChannelPut::channelCreated " << channel->getChannelName() << endl; - } - ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); - if(!putRequester) return; - chtype newType = getDBRType(pvRequest, channel->getNativeType()); - if(newType!=getType) { - getType = getDBRType(pvRequest, channel->getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); - bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); - PVStringPtr pvString = pvRequest->getSubField("record._options.block"); - if(pvString) { - std::string val = pvString->get(); - if(val.compare("true")==0) block = true; - } - bitSet->set(0); - } + notifyPutRequester = NotifyPutRequesterPtr(new NotifyPutRequester()); + notifyPutRequester->setChannelPut(shared_from_this()); EXCEPTION_GUARD(putRequester->channelPutConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } -void CAChannelPut::channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState) -{ - if(DEBUG_LEVEL>0) { - cout << "CAChannelPut::channelStateChange " << channel->getChannelName() << endl; - } - ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); - if(!putRequester) return; - if(connectionState==Channel::DISCONNECTED || connectionState==Channel::DESTROYED) { - EXCEPTION_GUARD(putRequester->channelDisconnect(connectionState==Channel::DESTROYED);) - } -} +std::string CAChannelPut::getRequesterName() { return "CAChannelPut";} -void CAChannelPut::channelDisconnect(bool destroy) -{ - if(DEBUG_LEVEL>0) { - cout << "CAChannelPut::channelDisconnect " << channel->getChannelName() << endl; - } - ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); - if(!putRequester) return; - EXCEPTION_GUARD(putRequester->channelDisconnect(destroy);) -} /* --------------- epics::pvAccess::ChannelPut --------------- */ @@ -1381,315 +585,105 @@ static void ca_put_handler(struct event_handler_args args) channelPut->putDone(args); } - static void ca_put_get_handler(struct event_handler_args args) { CAChannelPut *channelPut = static_cast(args.usr); channelPut->getDone(args); } -typedef int (*doPut)(CAChannel::shared_pointer const & channel, void *usrArg, PVStructure::shared_pointer const & from); - - -// template -template -int doPut_pvStructure(CAChannel::shared_pointer const & channel, void *usrArg, PVStructure::shared_pointer const & pvStructure) -{ - bool isScalarValue = pvStructure->getStructure()->getField("value")->getType() == scalar; - - if (isScalarValue) - { - std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getSubFieldT("value")); - - pT val = value->get(); - int result = 0; - if(usrArg!=NULL) { - result = ca_array_put_callback(channel->getNativeType(), 1, - channel->getChannelID(), &val, - ca_put_handler, usrArg); - } else { - result = ca_array_put(channel->getNativeType(), 1, - channel->getChannelID(), &val); - } - - if (result == ECA_NORMAL) - { - ca_flush_io(); - } - - return result; - } - else - { - std::tr1::shared_ptr value = pvStructure->getSubFieldT("value"); - - const pT* val = value->view().data(); - int result = 0; - if(usrArg!=NULL) { - result = ca_array_put_callback(channel->getNativeType(), - static_cast(value->getLength()), - channel->getChannelID(), val, - ca_put_handler, usrArg); - } else { - result = ca_array_put(channel->getNativeType(), - static_cast(value->getLength()), - channel->getChannelID(), val); - } - if (result == ECA_NORMAL) - { - ca_flush_io(); - } +} // namespace - return result; - } -} -// string specialization -template<> -int doPut_pvStructure(CAChannel::shared_pointer const & channel, void *usrArg, PVStructure::shared_pointer const & pvStructure) +void CAChannelPut::put(PVStructure::shared_pointer const & pvPutStructure, + BitSet::shared_pointer const & /*putBitSet*/) { - bool isScalarValue = pvStructure->getStructure()->getField("value")->getType() == scalar; - - if (isScalarValue) - { - std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getSubFieldT("value")); - - string val = value->get(); - int result = 0; - if(usrArg!=NULL) { - result = ca_array_put_callback( - channel->getNativeType(), 1, - channel->getChannelID(), val.c_str(), - ca_put_handler, usrArg); - } else { - result = ca_array_put( - channel->getNativeType(), 1, - channel->getChannelID(), val.c_str()); - } - if (result == ECA_NORMAL) - { - ca_flush_io(); - } - - return result; - } - else - { - std::tr1::shared_ptr value = pvStructure->getSubFieldT("value"); - - PVStringArray::const_svector stringArray(value->view()); - - size_t arraySize = stringArray.size(); - size_t ca_stringBufferSize = arraySize * MAX_STRING_SIZE; - char* ca_stringBuffer = new char[ca_stringBufferSize]; - memset(ca_stringBuffer, 0, ca_stringBufferSize); - - char *p = ca_stringBuffer; - for(size_t i = 0; i < arraySize; i++) - { - string value = stringArray[i]; - size_t len = value.length(); - if (len >= MAX_STRING_SIZE) - len = MAX_STRING_SIZE - 1; - memcpy(p, value.c_str(), len); - p += MAX_STRING_SIZE; - } - - int result = 0; - if(usrArg!=NULL) { - result = ca_array_put_callback( - channel->getNativeType(), arraySize, - channel->getChannelID(), ca_stringBuffer, - ca_put_handler, usrArg); - } else { - result = ca_array_put( - channel->getNativeType(), arraySize, - channel->getChannelID(), ca_stringBuffer); - } - delete[] ca_stringBuffer; - - if (result == ECA_NORMAL) - { - ca_flush_io(); - } - - return result; + if(DEBUG_LEVEL>1) { + cout << "CAChannelPut::put " << channel->getChannelName() << endl; } -} - -// enum specialization -template<> -int doPut_pvStructure(CAChannel::shared_pointer const & channel, void *usrArg, PVStructure::shared_pointer const & pvStructure) -{ - bool isScalarValue = pvStructure->getStructure()->getField("value")->getType() == structure; - - if (isScalarValue) + ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); + if(!putRequester) return; { - std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getSubFieldT("value.index")); - - dbr_enum_t val = value->get(); - int result = 0; - if(usrArg!=NULL) { - result = ca_array_put_callback( - channel->getNativeType(), 1, - channel->getChannelID(), &val, - ca_put_handler, usrArg); - } else { - result = ca_array_put( - channel->getNativeType(), 1, - channel->getChannelID(), &val); - } - if (result == ECA_NORMAL) - { - ca_flush_io(); - } - - return result; + Lock lock(mutex); + isPut = true; } - else - { - // no enum arrays in V3 - return ECA_NOSUPPORT; + putStatus = dbdToPv->putToDBD(channel,pvPutStructure,block,&ca_put_handler,this); + if(!block || !putStatus.isOK()) { + EXCEPTION_GUARD(putRequester->putDone(putStatus, shared_from_this())); } } -static doPut doPutFuncTable[] = -{ - doPut_pvStructure, // DBR_STRING - doPut_pvStructure, // DBR_INT, DBR_SHORT - doPut_pvStructure, // DBR_FLOAT - doPut_pvStructure, // DBR_ENUM - doPut_pvStructure, // DBR_CHAR -#if defined(__vxworks) || defined(__rtems__) - doPut_pvStructure, // DBR_LONG -#else - doPut_pvStructure, // DBR_LONG -#endif - doPut_pvStructure, // DBR_DOUBLE -}; - -} // namespace void CAChannelPut::putDone(struct event_handler_args &args) { - if(DEBUG_LEVEL>1) { + if(DEBUG_LEVEL>1) { cout << "CAChannelPut::putDone " << channel->getChannelName() << endl; } ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); if(!putRequester) return; - if (args.status == ECA_NORMAL) + if(args.status!=ECA_NORMAL) { - EXCEPTION_GUARD(putRequester->putDone(Status::Ok, shared_from_this())); - } - else - { - Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); - EXCEPTION_GUARD(putRequester->putDone(errorStatus, shared_from_this())); + putStatus = Status(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); + } else { + putStatus = Status::Ok; } + putDoneThread->putDone(notifyPutRequester); } -void CAChannelPut::put(PVStructure::shared_pointer const & pvPutStructure, - BitSet::shared_pointer const & /*putBitSet*/) +void CAChannelPut::getDone(struct event_handler_args &args) { - if(DEBUG_LEVEL>1) { - cout << "CAChannelPut::put " << channel->getChannelName() << endl; + if(DEBUG_LEVEL>1) { + cout << "CAChannelPut::getDone " << channel->getChannelName() << endl; } + ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); if(!putRequester) return; - doPut putFunc = doPutFuncTable[channel->getNativeType()]; - if (putFunc) - { - // TODO now we always put all - if(block) { - channel->attachContext(); - int result = putFunc(channel, this, pvPutStructure); - if (result != ECA_NORMAL) - { - string message(ca_message(result)); - Status errorStatus(Status::STATUSTYPE_ERROR, message); - EXCEPTION_GUARD(putRequester->putDone(errorStatus, shared_from_this())); - } - } else { - channel->attachContext(); - int result = putFunc(channel,NULL, pvPutStructure); - if (result == ECA_NORMAL) - { - EXCEPTION_GUARD(putRequester->putDone(Status::Ok, shared_from_this())); - } - else - { - string message(ca_message(result)); - Status errorStatus(Status::STATUSTYPE_ERROR,message); - EXCEPTION_GUARD(putRequester->putDone(errorStatus, shared_from_this())); - } - } - } - else - { - // TODO remove - std::cout << "no put func implemented" << std::endl; - } - + getStatus = dbdToPv->getFromDBD(pvStructure,bitSet,args); + putDoneThread->putDone(notifyPutRequester); } - -void CAChannelPut::getDone(struct event_handler_args &args) +void CAChannelPut::notifyClient() { - if(DEBUG_LEVEL>1) { - cout << "CAChannelPut::getDone " << channel->getChannelName() << endl; - } ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); if(!putRequester) return; - if (args.status == ECA_NORMAL) - { - copyDBRtoPVStructure copyFunc = copyFuncTable[getType]; - if (copyFunc) - copyFunc(args.dbr, args.count, pvStructure); - else - { - // TODO remove - std::cout << "no copy func implemented" << std::endl; - } - - EXCEPTION_GUARD(putRequester->getDone(Status::Ok, shared_from_this(), pvStructure, bitSet)); - } - else - { - Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); - EXCEPTION_GUARD(putRequester->getDone(errorStatus, shared_from_this(), - PVStructure::shared_pointer(), BitSet::shared_pointer())); + if(isPut) { + EXCEPTION_GUARD(putRequester->putDone(putStatus, shared_from_this())); + } else { + EXCEPTION_GUARD(putRequester->getDone(getStatus, shared_from_this(), pvStructure, bitSet)); } - } void CAChannelPut::get() { if(DEBUG_LEVEL>1) { - cout << "CAChannelPut::get " << channel->getChannelName() << endl; + std::cout << "CAChannelPut::get " << channel->getChannelName() << endl; } ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); if(!putRequester) return; - channel->attachContext(); + { + Lock lock(mutex); + isPut = false; + } - int result = ca_array_get_callback(getType, channel->getElementCount(), + channel->attachContext(); + bitSet->clear(); + int result = ca_array_get_callback(dbdToPv->getRequestType(), + 0, channel->getChannelID(), ca_put_get_handler, this); - if (result == ECA_NORMAL) { result = ca_flush_io(); } - if (result == ECA_NORMAL) return; - string message(ca_message(result)); - Status errorStatus(Status::STATUSTYPE_ERROR, message); - EXCEPTION_GUARD(putRequester->getDone(errorStatus, shared_from_this(), - PVStructure::shared_pointer(), BitSet::shared_pointer())); + if (result != ECA_NORMAL) + { + string mess("CAChannelPut::get "); + mess += channel->getChannelName() + " message " +ca_message(result); + Status status(Status::STATUSTYPE_ERROR,mess); + EXCEPTION_GUARD(putRequester->getDone(status, shared_from_this(), pvStructure, bitSet)); + } } - -/* --------------- epics::pvData::ChannelRequest --------------- */ - Channel::shared_pointer CAChannelPut::getChannel() { return channel; @@ -1720,7 +714,6 @@ class CACMonitorQueue : POINTER_DEFINITIONS(CACMonitorQueue); private: size_t queueSize; - bool overrunInProgress; bool isStarted; Mutex mutex; @@ -1729,7 +722,6 @@ class CACMonitorQueue : CACMonitorQueue( int32 queueSize) : queueSize(queueSize), - overrunInProgress(false), isStarted(false) {} ~CACMonitorQueue() @@ -1739,39 +731,29 @@ class CACMonitorQueue : { Lock guard(mutex); while(!monitorElementQueue.empty()) monitorElementQueue.pop(); - overrunInProgress = false; isStarted = true; } void stop() { Lock guard(mutex); while(!monitorElementQueue.empty()) monitorElementQueue.pop(); - overrunInProgress = false; isStarted = false; } - // return true if added to queue - bool event(const PVStructurePtr &pvStructure) + + bool event( + const PVStructurePtr &pvStructure, + const MonitorElementPtr & activeElement) { Lock guard(mutex); if(!isStarted) return false; - if(monitorElementQueue.size()==queueSize) - { - overrunInProgress = true; - return false; - } else { - PVStructure::shared_pointer pvs = - getPVDataCreate()->createPVStructure(pvStructure->getStructure()); - pvs->copy(*pvStructure); - MonitorElementPtr monitorElement(new MonitorElement(pvs)); - monitorElement->changedBitSet->set(0); - if(overrunInProgress) { - overrunInProgress = false; - monitorElement->overrunBitSet->set(0); - } - monitorElementQueue.push(monitorElement); - return true; - } - + if(monitorElementQueue.size()==queueSize) return false; + PVStructure::shared_pointer pvs = + getPVDataCreate()->createPVStructure(pvStructure); + MonitorElementPtr monitorElement(new MonitorElement(pvs)); + *(monitorElement->changedBitSet) = *(activeElement->changedBitSet); + *(monitorElement->overrunBitSet) = *(activeElement->overrunBitSet); + monitorElementQueue.push(monitorElement); + return true; } MonitorElementPtr poll() { @@ -1784,8 +766,10 @@ class CACMonitorQueue : void release(MonitorElementPtr const & monitorElement) { Lock guard(mutex); + if(!isStarted) return; if(monitorElementQueue.empty()) { - throw std::runtime_error("client error calling release"); + string mess("CAChannelMonitor::release client error calling release "); + throw std::runtime_error(mess); } monitorElementQueue.pop(); } @@ -1794,27 +778,14 @@ class CACMonitorQueue : CAChannelMonitorPtr CAChannelMonitor::create( CAChannel::shared_pointer const & channel, MonitorRequester::shared_pointer const & monitorRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) -{ - return CAChannelMonitorPtr(new CAChannelMonitor(channel, monitorRequester, pvRequest)); -} - -CAChannelMonitor::~CAChannelMonitor() + PVStructure::shared_pointer const & pvRequest) { if(DEBUG_LEVEL>0) { - std::cout << "CAChannelMonitor::~CAChannelMonitor() " << channel->getChannelName() << endl; + cout << "CAChannelMonitor::create " << channel->getChannelName() << endl; } - if(!isStarted) return; - channel->attachContext(); - int result = ca_clear_subscription(eventID); - if (result == ECA_NORMAL) return; - string mess("CAChannelMonitor::~CAChannelMonitor() "); - mess += ca_message(result); - cerr << mess << endl; + return CAChannelMonitorPtr(new CAChannelMonitor(channel, monitorRequester, pvRequest)); } -size_t CAChannelMonitor::num_instances; - CAChannelMonitor::CAChannelMonitor( CAChannel::shared_pointer const & channel, MonitorRequester::shared_pointer const & monitorRequester, @@ -1823,23 +794,32 @@ CAChannelMonitor::CAChannelMonitor( channel(channel), monitorRequester(monitorRequester), pvRequest(pvRequest), - isStarted(false) + isStarted(false), + monitorEventThread(MonitorEventThread::get()), + pevid(NULL) +{} + +CAChannelMonitor::~CAChannelMonitor() { if(DEBUG_LEVEL>0) { - cout << "CAChannelMonitor::CAChannelMonitor() " << channel->getChannelName() << endl; + std::cout << "CAChannelMonitor::~CAChannelMonitor() " + << channel->getChannelName() + << " isStarted " << (isStarted ? "true" : "false") + << endl; } + stop(); } void CAChannelMonitor::activate() { + MonitorRequester::shared_pointer requester(monitorRequester.lock()); + if(!requester) return; if(DEBUG_LEVEL>0) { std::cout << "CAChannelMonitor::activate " << channel->getChannelName() << endl; } - MonitorRequester::shared_pointer requester(monitorRequester.lock()); - if(!requester) return; - if(pvStructure) throw std::runtime_error("CAChannelMonitor::activate() was called twice"); - getType = getDBRType(pvRequest, channel->getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); + dbdToPv = DbdToPv::create(channel,pvRequest,monitorIO); + pvStructure = dbdToPv->createPVStructure(); + activeElement = MonitorElementPtr(new MonitorElement(pvStructure)); int32 queueSize = 2; PVStructurePtr pvOptions = pvRequest->getSubField("record._options"); if (pvOptions) { @@ -1852,129 +832,82 @@ void CAChannelMonitor::activate() if (size > 1) queueSize = size; } } + notifyMonitorRequester = NotifyMonitorRequesterPtr(new NotifyMonitorRequester()); + notifyMonitorRequester->setChannelMonitor(shared_from_this()); monitorQueue = CACMonitorQueuePtr(new CACMonitorQueue(queueSize)); - channel->addChannelMonitor(shared_from_this()); - if(channel->getConnectionState()==Channel::CONNECTED) { - EXCEPTION_GUARD(requester->monitorConnect(Status::Ok, shared_from_this(), - pvStructure->getStructure())); - } -} - -void CAChannelMonitor::channelCreated(const Status& status,Channel::shared_pointer const & c) -{ - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelMonitor::channelCreated " << channel->getChannelName() << endl; - } - MonitorRequester::shared_pointer requester(monitorRequester.lock()); - if(!requester) return; - chtype newType = getDBRType(pvRequest, channel->getNativeType()); - if(newType!=getType) { - getType = getDBRType(pvRequest, channel->getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); - int32 queueSize = 2; - PVStructurePtr pvOptions = pvRequest->getSubField("record._options"); - if (pvOptions) { - PVStringPtr pvString = pvOptions->getSubField("queueSize"); - if (pvString) { - int size; - std::stringstream ss; - ss << pvString->get(); - ss >> size; - if (size > 1) queueSize = size; - } - } - monitorQueue = CACMonitorQueuePtr(new CACMonitorQueue(queueSize)); - } EXCEPTION_GUARD(requester->monitorConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } -void CAChannelMonitor::channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState) -{ - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelMonitor::channelStateChange " << channel->getChannelName() << endl; - } - MonitorRequester::shared_pointer requester(monitorRequester.lock()); - if(!requester) return; - if(connectionState==Channel::DISCONNECTED || connectionState==Channel::DESTROYED) { - EXCEPTION_GUARD(requester->channelDisconnect(connectionState==Channel::DESTROYED);) - } -} - - -void CAChannelMonitor::channelDisconnect(bool destroy) -{ - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelMonitor::channelDisconnect " << channel->getChannelName() << endl; - } - MonitorRequester::shared_pointer requester(monitorRequester.lock()); - if(!requester) return; - EXCEPTION_GUARD(requester->channelDisconnect(destroy);) -} +std::string CAChannelMonitor::getRequesterName() { return "CAChannelMonitor";} void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args) { if(DEBUG_LEVEL>1) { - std::cout << "CAChannelMonitor::subscriptionEvent " << channel->getChannelName() << endl; + std::cout << "CAChannelMonitor::subscriptionEvent " + << channel->getChannelName() << endl; + } + { + Lock lock(mutex); + if(!isStarted) return; } MonitorRequester::shared_pointer requester(monitorRequester.lock()); if(!requester) return; - if (args.status == ECA_NORMAL) + Status status = dbdToPv->getFromDBD(pvStructure,activeElement->changedBitSet,args); + if(status.isOK()) { - copyDBRtoPVStructure copyFunc = copyFuncTable[getType]; - if (copyFunc) { - copyFunc(args.dbr, args.count, pvStructure); - monitorQueue->event(pvStructure); - // call monitorRequester even if queue is full - requester->monitorEvent(shared_from_this()); + if(monitorQueue->event(pvStructure,activeElement)) { + activeElement->changedBitSet->clear(); + activeElement->overrunBitSet->clear(); } else { - std::cout << "no copy func implemented" << std::endl; - + *(activeElement->overrunBitSet) |= *(activeElement->changedBitSet); } + monitorEventThread->event(notifyMonitorRequester); } else { string mess("CAChannelMonitor::subscriptionEvent "); + mess += channel->getChannelName(); mess += ca_message(args.status); throw std::runtime_error(mess); } } -epics::pvData::Status CAChannelMonitor::start() + +void CAChannelMonitor::notifyClient() +{ + { + Lock lock(mutex); + if(!isStarted) return; + } + MonitorRequester::shared_pointer requester(monitorRequester.lock()); + if(!requester) return; + requester->monitorEvent(shared_from_this()); +} + +Status CAChannelMonitor::start() { if(DEBUG_LEVEL>0) { std::cout << "CAChannelMonitor::start " << channel->getChannelName() << endl; } Status status = Status::Ok; - if(isStarted) { - status = Status(Status::STATUSTYPE_WARNING,"already started"); - return status; + { + Lock lock(mutex); + if(isStarted) { + status = Status(Status::STATUSTYPE_WARNING,"already started"); + return status; + } + isStarted = true; + monitorQueue->start(); } channel->attachContext(); - - /* - From R3.14.12 onwards when using the IOC server and the C++ client libraries monitor callbacks - replies will give a CA client application the current number of elements in an array field, - provided they specified an element count of zero in their original request. - The element count is passed in the callback argument structure. - Prior to R3.14.12 you could request a zero-length subscription and the zero would mean - “use the value of chid->element_count() for this particular channel”, - but the length of the data you got in your callbacks would never change - (the server would zero-fill everything after the current length of the field). - */ - - // TODO DBE_PROPERTY support - int result = ca_create_subscription(getType, + int result = ca_create_subscription(dbdToPv->getRequestType(), 0, channel->getChannelID(), DBE_VALUE, ca_subscription_handler, this, - &eventID); + &pevid); if (result == ECA_NORMAL) { - isStarted = true; - monitorQueue->start(); result = ca_flush_io(); } if (result == ECA_NORMAL) return status; @@ -1983,25 +916,23 @@ epics::pvData::Status CAChannelMonitor::start() return Status(Status::STATUSTYPE_ERROR,message); } -epics::pvData::Status CAChannelMonitor::stop() +Status CAChannelMonitor::stop() { if(DEBUG_LEVEL>0) { - std::cout << "CAChannelMonitor::stop " << channel->getChannelName() << endl; + std::cout << "CAChannelMonitor::stop " + << channel->getChannelName() + << " isStarted " << (isStarted ? "true" : "false") + << endl; } - Status status = Status::Ok; - if(!isStarted) return Status(Status::STATUSTYPE_WARNING,"already stopped"); - channel->attachContext(); - int result = ca_clear_subscription(eventID); - if (result == ECA_NORMAL) { - isStarted = false; - monitorQueue->stop(); - result = ca_flush_io(); + Lock lock(mutex); + if(!isStarted) return Status(Status::STATUSTYPE_WARNING,"already stopped"); + isStarted = false; } - if (result == ECA_NORMAL) return status; - string mess("CAChannelMonitor::stop() "); - mess += ca_message(result); - return Status(Status::STATUSTYPE_ERROR,mess); + monitorQueue->stop(); + int result = ca_clear_subscription(pevid); + if(result==ECA_NORMAL) return Status::Ok; + return Status(Status::STATUSTYPE_ERROR,string(ca_message(result))); } @@ -2010,6 +941,10 @@ MonitorElementPtr CAChannelMonitor::poll() if(DEBUG_LEVEL>1) { std::cout << "CAChannelMonitor::poll " << channel->getChannelName() << endl; } + { + Lock lock(mutex); + if(!isStarted) return MonitorElementPtr(); + } return monitorQueue->poll(); } @@ -2022,7 +957,7 @@ void CAChannelMonitor::release(MonitorElementPtr const & monitorElement) monitorQueue->release(monitorElement); } -/* --------------- epics::pvData::ChannelRequest --------------- */ +/* --------------- ChannelRequest --------------- */ void CAChannelMonitor::cancel() { diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index 4a2c40cb..82286278 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -4,6 +4,11 @@ * in file LICENSE that is included with this distribution. */ +/** + * @author msekoranja, mrk + * @date 2018.07 + */ + #ifndef CACHANNEL_H #define CACHANNEL_H @@ -11,17 +16,34 @@ #include #include - +#include /* for CA */ #include #include "caProviderPvt.h" +#include "dbdToPv.h" namespace epics { namespace pvAccess { namespace ca { +class NotifyMonitorRequester; +typedef std::tr1::shared_ptr NotifyMonitorRequesterPtr; +class MonitorEventThread; +typedef std::tr1::shared_ptr MonitorEventThreadPtr; + +class NotifyGetRequester; +typedef std::tr1::shared_ptr NotifyGetRequesterPtr; +typedef std::tr1::weak_ptr NotifyGetRequesterWPtr; +class GetDoneThread; +typedef std::tr1::shared_ptr GetDoneThreadPtr; + +class NotifyPutRequester; +typedef std::tr1::shared_ptr NotifyPutRequesterPtr; +typedef std::tr1::weak_ptr NotifyPutRequesterWPtr; +class PutDoneThread; +typedef std::tr1::shared_ptr PutDoneThreadPtr; class CAChannelGetField; typedef std::tr1::shared_ptr CAChannelGetFieldPtr; @@ -53,220 +75,140 @@ class CAChannel : public Channel, public std::tr1::enable_shared_from_this { - public: POINTER_DEFINITIONS(CAChannel); - - static size_t num_instances; - - static CAChannelPtr create(CAChannelProvider::shared_pointer const & channelProvider, - std::string const & channelName, - short priority, - ChannelRequester::shared_pointer const & channelRequester); - + static CAChannelPtr create( + CAChannelProvider::shared_pointer const & channelProvider, + std::string const & channelName, + short priority, + ChannelRequester::shared_pointer const & channelRequester); virtual ~CAChannel(); void connected(); void disconnected(); - chid getChannelID(); - chtype getNativeType(); - unsigned getElementCount(); - epics::pvData::Structure::const_shared_pointer getStructure(); - - /* --------------- epics::pvAccess::Channel --------------- */ virtual std::tr1::shared_ptr getProvider(); virtual std::string getRemoteAddress(); virtual ConnectionState getConnectionState(); virtual std::string getChannelName(); virtual std::tr1::shared_ptr getChannelRequester(); - virtual void getField(GetFieldRequester::shared_pointer const & requester,std::string const & subField); - virtual AccessRights getAccessRights(epics::pvData::PVField::shared_pointer const & pvField); - virtual ChannelGet::shared_pointer createChannelGet( ChannelGetRequester::shared_pointer const & channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); - + epics::pvData::PVStructurePtr const & pvRequest); virtual ChannelPut::shared_pointer createChannelPut( ChannelPutRequester::shared_pointer const & channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); - + epics::pvData::PVStructurePtr const & pvRequest); virtual Monitor::shared_pointer createMonitor( MonitorRequester::shared_pointer const & monitorRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); - + epics::pvData::PVStructurePtr const & pvRequest); virtual void printInfo(std::ostream& out); - /* ---------------------------------------------------------------- */ - void attachContext(); - - void addChannelGet(const CAChannelGetPtr & get); - void addChannelPut(const CAChannelPutPtr & get); - void addChannelMonitor(const CAChannelMonitorPtr & get); void disconnectChannel(); - - private: - - /* --------------- Destroyable --------------- */ - virtual void destroy() {} - CAChannel(std::string const & channelName, CAChannelProvider::shared_pointer const & channelProvider, ChannelRequester::shared_pointer const & channelRequester); void activate(short priority); + void addMonitor(CAChannelMonitorPtr const & monitor); std::string channelName; - CAChannelProviderWPtr channelProvider; ChannelRequester::weak_pointer channelRequester; - chid channelID; - chtype channelType; - unsigned elementCount; bool channelCreated; - epics::pvData::Structure::const_shared_pointer structure; epics::pvData::Mutex requestsMutex; - std::queue getFieldQueue; std::queue putQueue; std::queue getQueue; std::queue monitorQueue; - std::vector getList; - std::vector putList; - std::vector monitorList; + std::vector monitorlist; }; class CAChannelGet : public ChannelGet, - public ChannelRequester, - public ChannelBaseRequester, public std::tr1::enable_shared_from_this { - public: POINTER_DEFINITIONS(CAChannelGet); - - static size_t num_instances; - static CAChannelGet::shared_pointer create(CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); - + epics::pvData::PVStructurePtr const & pvRequest); virtual ~CAChannelGet(); - void getDone(struct event_handler_args &args); - - /* --------------- epics::pvAccess::ChannelGet --------------- */ virtual void get(); - - /* --------------- epics::pvData::ChannelRequest --------------- */ virtual Channel::shared_pointer getChannel(); virtual void cancel(); virtual void lastRequest(); - - /* --------------- ChannelRequester --------------- */ - virtual void channelCreated( - const epics::pvData::Status& status, - Channel::shared_pointer const & channel); - virtual void channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState); - virtual std::string getRequesterName() { return "CAChannelGet";} - /* --------------- ChannelBaseRequester --------------- */ - virtual void channelDisconnect(bool destroy); + virtual std::string getRequesterName(); void activate(); - + void notifyClient(); private: - /* --------------- Destroyable --------------- */ - virtual void destroy() {} - + virtual void destroy() {} CAChannelGet(CAChannel::shared_pointer const & _channel, ChannelGetRequester::shared_pointer const & _channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); + epics::pvData::PVStructurePtr const & pvRequest); CAChannelPtr channel; ChannelGetRequester::weak_pointer channelGetRequester; - const epics::pvData::PVStructure::shared_pointer pvRequest; - - chtype getType; + epics::pvData::PVStructurePtr const & pvRequest; + epics::pvData::Status getStatus; + GetDoneThreadPtr getDoneThread; + NotifyGetRequesterPtr notifyGetRequester; + DbdToPvPtr dbdToPv; + epics::pvData::Mutex mutex; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::BitSet::shared_pointer bitSet; }; - - class CAChannelPut : public ChannelPut, - public ChannelRequester, - public ChannelBaseRequester, public std::tr1::enable_shared_from_this { public: POINTER_DEFINITIONS(CAChannelPut); - - static size_t num_instances; - static CAChannelPut::shared_pointer create(CAChannel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); - + epics::pvData::PVStructurePtr const & pvRequest); virtual ~CAChannelPut(); - void putDone(struct event_handler_args &args); void getDone(struct event_handler_args &args); - - /* --------------- epics::pvAccess::ChannelPut --------------- */ - virtual void put( epics::pvData::PVStructure::shared_pointer const & pvPutStructure, epics::pvData::BitSet::shared_pointer const & putBitSet ); virtual void get(); - - /* --------------- epics::pvData::ChannelRequest --------------- */ - virtual Channel::shared_pointer getChannel(); virtual void cancel(); virtual void lastRequest(); - /* --------------- ChannelRequester --------------- */ - virtual void channelCreated( - const epics::pvData::Status& status, - Channel::shared_pointer const & channel); - virtual void channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState); - virtual std::string getRequesterName() { return "CAChannelPut";} - /* --------------- ChannelBaseRequester --------------- */ - virtual void channelDisconnect(bool destroy); - - void activate(); - + virtual std::string getRequesterName(); + void activate(); + void notifyClient(); private: - /* --------------- Destroyable --------------- */ - - virtual void destroy() {} - + virtual void destroy() {} CAChannelPut(CAChannel::shared_pointer const & _channel, ChannelPutRequester::shared_pointer const & _channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); - + epics::pvData::PVStructurePtr const & pvRequest); CAChannelPtr channel; ChannelPutRequester::weak_pointer channelPutRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; bool block; - - chtype getType; + bool isPut; + epics::pvData::Status getStatus; + epics::pvData::Status putStatus; + PutDoneThreadPtr putDoneThread; + NotifyPutRequesterPtr notifyPutRequester; + DbdToPvPtr dbdToPv; + epics::pvData::Mutex mutex; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::BitSet::shared_pointer bitSet; }; @@ -276,66 +218,46 @@ typedef std::tr1::shared_ptr CACMonitorQueuePtr; class CAChannelMonitor : public Monitor, - public ChannelRequester, - public ChannelBaseRequester, public std::tr1::enable_shared_from_this { public: POINTER_DEFINITIONS(CAChannelMonitor); - - static size_t num_instances; - static CAChannelMonitor::shared_pointer create(CAChannel::shared_pointer const & channel, MonitorRequester::shared_pointer const & monitorRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); - + epics::pvData::PVStructurePtr const & pvRequest); virtual ~CAChannelMonitor(); - void subscriptionEvent(struct event_handler_args &args); - /* --------------- Monitor --------------- */ - virtual epics::pvData::Status start(); virtual epics::pvData::Status stop(); virtual MonitorElementPtr poll(); virtual void release(MonitorElementPtr const & monitorElement); - - /* --------------- epics::pvData::ChannelRequest --------------- */ virtual void cancel(); - /* --------------- ChannelRequester --------------- */ - virtual void channelCreated( - const epics::pvData::Status& status, - Channel::shared_pointer const & channel); - virtual void channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState); - virtual std::string getRequesterName() { return "CAChannelMonitor";} - /* --------------- ChannelBaseRequester --------------- */ - virtual void channelDisconnect(bool destroy); + virtual std::string getRequesterName(); void activate(); + void notifyClient(); private: - /* --------------- Destroyable --------------- */ virtual void destroy() {} - CAChannelMonitor(CAChannel::shared_pointer const & _channel, MonitorRequester::shared_pointer const & _monitorRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); - - + epics::pvData::PVStructurePtr const & pvRequest); CAChannelPtr channel; MonitorRequester::weak_pointer monitorRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; bool isStarted; - chtype getType; + MonitorEventThreadPtr monitorEventThread; + evid pevid; + NotifyMonitorRequesterPtr notifyMonitorRequester; + DbdToPvPtr dbdToPv; + epics::pvData::Mutex mutex; epics::pvData::PVStructure::shared_pointer pvStructure; - evid eventID; + epics::pvData::MonitorElementPtr activeElement; + CACMonitorQueuePtr monitorQueue; }; -} -} -} +}}} #endif /* CACHANNEL_H */ diff --git a/src/ca/caProvider.cpp b/src/ca/caProvider.cpp index 2c459f94..5c5b22ca 100644 --- a/src/ca/caProvider.cpp +++ b/src/ca/caProvider.cpp @@ -4,16 +4,16 @@ * in file LICENSE that is included with this distribution. */ -#include - #include #include #include #include #include -#include #include -#include + +#include "monitorEventThread.h" +#include "getDoneThread.h" +#include "putDoneThread.h" #define epicsExportSharedSymbols #include @@ -31,8 +31,6 @@ using namespace epics::pvData; catch (std::exception &e) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d: %s", __FILE__, __LINE__, e.what()); } \ catch (...) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d.", __FILE__, __LINE__); } -size_t CAChannelProvider::num_instances; - CAChannelProvider::CAChannelProvider() : current_context(0) { @@ -40,13 +38,14 @@ CAChannelProvider::CAChannelProvider() } CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr&) - : current_context(0) + : current_context(0), + monitorEventThread(MonitorEventThread::get()), + getDoneThread(GetDoneThread::get()), + putDoneThread(PutDoneThread::get()) { if(DEBUG_LEVEL>0) { std::cout<< "CAChannelProvider::CAChannelProvider\n"; } - // Ignoring Configuration as CA only allows config via. environment, - // and we don't want to change this here. initialize(); } @@ -60,7 +59,8 @@ CAChannelProvider::~CAChannelProvider() std::queue channelQ; { Lock lock(channelListMutex); - for(size_t i=0; i< caChannelList.size(); ++i) { + for(size_t i=0; i< caChannelList.size(); ++i) + { CAChannelPtr caChannel(caChannelList[i].lock()); if(caChannel) channelQ.push(caChannel); } @@ -68,16 +68,21 @@ CAChannelProvider::~CAChannelProvider() } while(!channelQ.empty()) { if(DEBUG_LEVEL>0) { - std::cout << "disconnectAllChannels calling disconnectChannel " + std::cout << "~CAChannelProvider() calling disconnectChannel " << channelQ.front()->getChannelName() << std::endl; } channelQ.front()->disconnectChannel(); channelQ.pop(); } - attachContext(); - ca_flush_io(); + monitorEventThread->stop(); + getDoneThread->stop(); + putDoneThread->stop(); + if(DEBUG_LEVEL>0) { + std::cout << "CAChannelProvider::~CAChannelProvider() calling ca_context_destroy\n"; + } ca_context_destroy(); +//std::cout << "CAChannelProvider::~CAChannelProvider() returning\n"; } std::string CAChannelProvider::getProviderName() @@ -90,12 +95,12 @@ ChannelFind::shared_pointer CAChannelProvider::channelFind( ChannelFindRequester::shared_pointer const & channelFindRequester) { if (channelName.empty()) - throw std::invalid_argument("empty channel name"); + throw std::invalid_argument("CAChannelProvider::channelFind empty channel name"); if (!channelFindRequester) - throw std::invalid_argument("null requester"); + throw std::invalid_argument("CAChannelProvider::channelFind null requester"); - Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented"); + Status errorStatus(Status::STATUSTYPE_ERROR, "CAChannelProvider::channelFind not implemented"); ChannelFind::shared_pointer nullChannelFind; EXCEPTION_GUARD(channelFindRequester->channelFindResult(errorStatus, nullChannelFind, false)); return nullChannelFind; @@ -105,9 +110,9 @@ ChannelFind::shared_pointer CAChannelProvider::channelList( ChannelListRequester::shared_pointer const & channelListRequester) { if (!channelListRequester.get()) - throw std::runtime_error("null requester"); + throw std::runtime_error("CAChannelProvider::channelList null requester"); - Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented"); + Status errorStatus(Status::STATUSTYPE_ERROR, "CAChannelProvider::channelList not implemented"); ChannelFind::shared_pointer nullChannelFind; PVStringArray::const_svector none; EXCEPTION_GUARD(channelListRequester->channelListResult(errorStatus, nullChannelFind, none, false)); @@ -131,7 +136,7 @@ Channel::shared_pointer CAChannelProvider::createChannel( std::string const & address) { if (!address.empty()) - throw std::invalid_argument("CA does not support 'address' parameter"); + throw std::invalid_argument("CAChannelProvider::createChannel does not support 'address' parameter"); return CAChannel::create(shared_from_this(), channelName, priority, channelRequester); } @@ -165,31 +170,29 @@ void CAChannelProvider::poll() { } - void CAChannelProvider::attachContext() { ca_client_context* thread_context = ca_current_context(); if (thread_context == current_context) return; if (thread_context != NULL) { - throw std::runtime_error("CAChannelProvider: Foreign CA context in use"); + throw std::runtime_error("CAChannelProvider::attachContext Foreign CA context in use"); } int result = ca_attach_context(current_context); if (result != ECA_NORMAL) { - std::cout << - "CA error %s occurred while calling ca_attach_context:" - << ca_message(result) << std::endl; + std::string mess("CAChannelProvider::attachContext error calling ca_attach_context "); + mess += ca_message(result); + throw std::runtime_error(mess); } } void CAChannelProvider::initialize() { if(DEBUG_LEVEL>0) std::cout << "CAChannelProvider::initialize()\n"; - /* Create Channel Access */ int result = ca_context_create(ca_enable_preemptive_callback); if (result != ECA_NORMAL) { - throw std::runtime_error( - std::string("CA error %s occurred while trying to start channel access:") - + ca_message(result)); + std::string mess("CAChannelProvider::initialize error calling ca_context_create "); + mess += ca_message(result); + throw std::runtime_error(mess); } current_context = ca_current_context(); } @@ -198,17 +201,10 @@ void CAClientFactory::start() { if(DEBUG_LEVEL>0) std::cout << "CAClientFactory::start()\n"; if(ChannelProviderRegistry::clients()->getProvider("ca")) { - // do not start twice return; } epicsSignalInstallSigAlarmIgnore(); epicsSignalInstallSigPipeIgnore(); - registerRefCounter("CAChannelProvider", &CAChannelProvider::num_instances); - registerRefCounter("CAChannel", &CAChannel::num_instances); - registerRefCounter("CAChannelGet", &CAChannelGet::num_instances); - registerRefCounter("CAChannelPut", &CAChannelPut::num_instances); - registerRefCounter("CAChannelMonitor", &CAChannelMonitor::num_instances); - if(!ChannelProviderRegistry::clients()->add("ca", true)) { throw std::runtime_error("CAClientFactory::start failed"); @@ -220,8 +216,5 @@ void CAClientFactory::stop() // unregister now done with exit hook } - -} -} -} +}}} diff --git a/src/ca/caProviderPvt.h b/src/ca/caProviderPvt.h index 7f656b84..0d48271d 100644 --- a/src/ca/caProviderPvt.h +++ b/src/ca/caProviderPvt.h @@ -4,6 +4,11 @@ * in file LICENSE that is included with this distribution. */ +/** + * @author msekoranja, mrk + * @date 2018.07 + */ + #ifndef CAPROVIDERPVT_H #define CAPROVIDERPVT_H @@ -19,6 +24,15 @@ namespace ca { #define DEBUG_LEVEL 0 +class MonitorEventThread; +typedef std::tr1::shared_ptr MonitorEventThreadPtr; + +class GetDoneThread; +typedef std::tr1::shared_ptr GetDoneThreadPtr; + +class PutDoneThread; +typedef std::tr1::shared_ptr PutDoneThreadPtr; + class CAChannel; typedef std::tr1::shared_ptr CAChannelPtr; typedef std::tr1::weak_ptr CAChannelWPtr; @@ -33,9 +47,6 @@ class CAChannelProvider : { public: POINTER_DEFINITIONS(CAChannelProvider); - - static size_t num_instances; - CAChannelProvider(); CAChannelProvider(const std::tr1::shared_ptr&); virtual ~CAChannelProvider(); @@ -66,22 +77,20 @@ class CAChannelProvider : virtual void flush(); virtual void poll(); - void addChannel(const CAChannelPtr & channel); - - /* ---------------------------------------------------------------- */ - void attachContext(); - + void addChannel(const CAChannelPtr & channel); private: + virtual void destroy() EPICS_DEPRECATED {} void initialize(); ca_client_context* current_context; epics::pvData::Mutex channelListMutex; std::vector caChannelList; + MonitorEventThreadPtr monitorEventThread; + GetDoneThreadPtr getDoneThread; + PutDoneThreadPtr putDoneThread; }; -} -} -} +}}} #endif /* CAPROVIDERPVT_H */ diff --git a/src/ca/caStatus.cpp b/src/ca/caStatus.cpp deleted file mode 100644 index 3e9d352b..00000000 --- a/src/ca/caStatus.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * pvAccessCPP is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ - -#define epicsExportSharedSymbols -#include - -namespace epics { -namespace pvAccess { -namespace ca { - -epicsShareDef std::string dbrStatus2alarmMessage[] = { - "NO_ALARM", // 0 .. - "READ_ALARM", - "WRITE_ALARM", - "HIHI_ALARM", - "HIGH_ALARM", - "LOLO_ALARM", - "LOW_ALARM", - "STATE_ALARM", - "COS_ALARM", - "COMM_ALARM", - "TIMEOUT_ALARM", - "HW_LIMIT_ALARM", - "CALC_ALARM", - "SCAN_ALARM", - "LINK_ALARM", - "SOFT_ALARM", - "BAD_SUB_ALARM", - "UDF_ALARM", - "DISABLE_ALARM", - "SIMM_ALARM", - "READ_ACCESS_ALARM", - "WRITE_ACCESS_ALARM" // .. 21 -}; - -epicsShareDef int dbrStatus2alarmStatus[] = { - noStatus, //"NO_ALARM" - driverStatus, //"READ_ALARM", - driverStatus, //"WRITE_ALARM", - recordStatus, //"HIHI_ALARM", - recordStatus, //"HIGH_ALARM", - recordStatus, //"LOLO_ALARM", - recordStatus, //"LOW_ALARM", - recordStatus, //"STATE_ALARM", - recordStatus, //"COS_ALARM", - driverStatus, //"COMM_ALARM", - driverStatus, //"TIMEOUT_ALARM", - deviceStatus, //"HW_LIMIT_ALARM", - recordStatus, //"CALC_ALARM", - dbStatus, //"SCAN_ALARM", - dbStatus, //"LINK_ALARM", - dbStatus, //"SOFT_ALARM", - confStatus, //"BAD_SUB_ALARM", - recordStatus, //"UDF_ALARM", - recordStatus, //"DISABLE_ALARM", - recordStatus, //"SIMM_ALARM", - clientStatus, //"READ_ACCESS_ALARM", - clientStatus //"WRITE_ACCESS_ALARM" // .. 21 -}; - - -} -} -} - - diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp new file mode 100644 index 00000000..636dc5eb --- /dev/null +++ b/src/ca/dbdToPv.cpp @@ -0,0 +1,803 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvAccessCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "caChannel.h" +#define epicsExportSharedSymbols +#include "dbdToPv.h" + +using namespace epics::pvData; +using std::string; +using std::ostringstream; + +namespace epics { +namespace pvAccess { +namespace ca { + +#define CA_PRIORITY 50 + +static void enumChoicesHandler(struct event_handler_args args) +{ + DbdToPv *dbdToPv = static_cast(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, + PVStructurePtr const & pvRequest, + IOType ioType) +{ + DbdToPvPtr dbdToPv(new DbdToPv(ioType)); + dbdToPv->activate(caChannel,pvRequest); + return dbdToPv; +} + +DbdToPv::DbdToPv(IOType ioType) +: ioType(ioType), + fieldRequested(false), + alarmRequested(false), + timeStampRequested(false), + displayRequested(false), + controlRequested(false), + valueAlarmRequested(false), + isArray(false), + firstTime(true), + choicesValid(false), + waitForChoicesValid(false), + caValueType(-1), + caRequestType(-1), + maxElements(0) +{ + caTimeStamp.secPastEpoch = 0; + caTimeStamp.nsec = 0; +} + +static ScalarType dbr2ST[] = +{ + pvString, // DBR_STRING = 0 + pvShort, // DBR_SHORT. DBR_INT = 1 + pvFloat, // DBR_FLOAT = 2 + static_cast(-1), // DBR_ENUM = 3 + pvByte, // DBR_CHAR = 4 + pvInt, // DBR_LONG = 5 + pvDouble // DBR_DOUBLE = 6 +}; + +static chtype getDbrType(const ScalarType scalarType) +{ + switch(scalarType) + { + case pvString : return DBR_STRING; + case pvByte : return DBR_CHAR; + case pvShort : return DBR_SHORT; + case pvInt : return DBR_LONG; + case pvFloat : return DBR_FLOAT; + case pvDouble : return DBR_DOUBLE; + default: break; + } + throw std::runtime_error("getDbr: illegal scalarType"); +} + + +void DbdToPv::activate( + CAChannelPtr const & caChannel, + PVStructurePtr const & pvRequest) +{ + chid channelID = caChannel->getChannelID(); + chtype channelType = ca_field_type(channelID); + caValueType = (channelType==DBR_ENUM ? DBR_ENUM : getDbrType(dbr2ST[channelType])); + if(!pvRequest) { + string mess(caChannel->getChannelName()); + mess += " DbdToPv::activate pvRequest is null"; + throw std::runtime_error(mess); + } + PVStructurePtr fieldPVStructure; + if(pvRequest->getPVFields().size()==0) { + fieldPVStructure = pvRequest; + } else { + fieldPVStructure = pvRequest->getSubField("field"); + } + if(!fieldPVStructure) { + ostringstream mess; + mess << caChannel->getChannelName() + << " DbdToPv::activate illegal pvRequest " << pvRequest; + throw std::runtime_error(mess.str()); + } + if(fieldPVStructure->getPVFields().size()==0) + { + fieldRequested = true; + alarmRequested = true; + timeStampRequested = true; + displayRequested = true; + controlRequested = true; + valueAlarmRequested = true; + } else { + if(fieldPVStructure->getSubField("value")) fieldRequested = true; + if(fieldPVStructure->getSubField("alarm")) alarmRequested = true; + if(fieldPVStructure->getSubField("timeStamp")) timeStampRequested = true; + if(fieldPVStructure->getSubField("display")) displayRequested = true; + if(fieldPVStructure->getSubField("control")) controlRequested = true; + if(fieldPVStructure->getSubField("valueAlarm")) valueAlarmRequested = true; + } + switch(ioType) + { + case getIO : break; + case putIO: + alarmRequested = false; + timeStampRequested = false; + displayRequested = false; + controlRequested = false; + valueAlarmRequested = false; + break; + case monitorIO: break; + } + StandardFieldPtr standardField = getStandardField(); + if(channelType==DBR_ENUM) + { + displayRequested = false; + controlRequested = false; + valueAlarmRequested = false; + string properties; + if(alarmRequested && timeStampRequested) { + properties += "alarm,timeStamp"; + } else if(timeStampRequested) { + properties += "timeStamp"; + } else if(alarmRequested) { + properties += "alarm"; + } + caRequestType = (properties.size()==0 ? DBR_ENUM : DBR_TIME_ENUM); + structure = standardField->enumerated(properties); + int result = ca_array_get_callback(DBR_GR_ENUM, + 1, + channelID, enumChoicesHandler, this); + if (result == ECA_NORMAL) result = ca_flush_io(); + if (result != ECA_NORMAL) { + string mess(caChannel->getChannelName()); + mess += " DbdToPv::activate getting enum cnoices "; + mess += ca_message(result); + throw std::runtime_error(mess); + } + // NOTE: we do not wait here, since all subsequent request (over TCP) is serialized + // and will guarantee that enumChoicesHandler is called first + return; + } + maxElements = ca_element_count(channelID); + if(maxElements!=1) isArray = true; + if(isArray) + { + controlRequested = false; + valueAlarmRequested = false; + } + ScalarType st = dbr2ST[channelType]; + if(st==pvString) { + displayRequested = false; + controlRequested = false; + valueAlarmRequested = false; + } + if(controlRequested || displayRequested || valueAlarmRequested) timeStampRequested = false; + FieldCreatePtr fieldCreate(FieldCreate::getFieldCreate()); + PVDataCreatePtr pvDataCreate(PVDataCreate::getPVDataCreate()); + FieldBuilderPtr fieldBuilder(fieldCreate->createFieldBuilder()); + if(fieldRequested) { + if(isArray) { + fieldBuilder->addArray("value",st); + } else { + fieldBuilder->add("value",st); + } + } + if(alarmRequested) fieldBuilder->add("alarm",standardField->alarm()); + if(timeStampRequested) fieldBuilder->add("timeStamp",standardField->timeStamp()); + if(displayRequested) fieldBuilder->add("display",standardField->display()); + if(controlRequested) fieldBuilder->add("control",standardField->control()); + if(valueAlarmRequested) { + switch(st) + { + case pvByte: + fieldBuilder->add("valueAlarm",standardField->byteAlarm()); break; + case pvShort: + fieldBuilder->add("valueAlarm",standardField->shortAlarm()); break; + case pvInt: + fieldBuilder->add("valueAlarm",standardField->intAlarm()); break; + case pvFloat: + fieldBuilder->add("valueAlarm",standardField->floatAlarm()); break; + case pvDouble: + fieldBuilder->add("valueAlarm",standardField->doubleAlarm()); break; + default: + throw std::runtime_error("DbDToPv::activate: bad type"); + } + } + structure = fieldBuilder->createStructure(); + caRequestType = caValueType; + if(displayRequested || controlRequested || valueAlarmRequested) + { + caRequestType = dbf_type_to_DBR_CTRL(caValueType); + } else if(timeStampRequested || alarmRequested) { + caRequestType = dbf_type_to_DBR_TIME(caValueType); + } else { + caRequestType = dbf_type_to_DBR(caValueType); + } + if(displayRequested) { + chid channelID; + string name(caChannel->getChannelName() + ".DESC"); + int result = ca_create_channel(name.c_str(), + description_connection_handler, + this, + CA_PRIORITY, // TODO mapping + &channelID); + if (result == ECA_NORMAL) result = ca_flush_io(); + if (result != ECA_NORMAL) { + string mess(caChannel->getChannelName()); + mess += " DbdToPv::activate getting description "; + mess += ca_message(result); + throw std::runtime_error(mess); + } + } +} + +void DbdToPv::descriptionConnected(struct connection_handler_args args) +{ + if (args.op != CA_OP_CONN_UP) return; + ca_array_get_callback(DBR_STRING, + 0, + args.chid, descriptionHandler, this); +} + +void DbdToPv::getDescriptionDone(struct event_handler_args &args) +{ + if(args.status!=ECA_NORMAL) return; + const dbr_string_t *value = static_cast(dbr_value_ptr(args.dbr,DBR_STRING)); + description = string(*value); + ca_clear_channel(args.chid); +} + +void DbdToPv::getChoicesDone(struct event_handler_args &args) +{ + if(args.status!=ECA_NORMAL) + { + string message("DbdToPv::getChoicesDone ca_message "); + message += ca_message(args.status); + throw std::runtime_error(message); + } + const dbr_gr_enum* dbr_enum_p = static_cast(args.dbr); + 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(); +} + +chtype DbdToPv::getRequestType() +{ + if(caRequestType<0) { + throw std::runtime_error("DbDToPv::getRequestType: bad type"); + } + return caRequestType; +} + +PVStructurePtr DbdToPv::createPVStructure() +{ + return getPVDataCreate()->createPVStructure(structure); +} + +template +void copy_DBRScalar(const void * dbr, PVScalar::shared_pointer const & pvScalar) +{ + std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvScalar); + value->put(static_cast(dbr)[0]); +} + +template +void copy_DBRScalarArray(const void * dbr, unsigned count, PVScalarArray::shared_pointer const & pvArray) +{ + std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvArray); + typename pvT::svector temp(value->reuse()); + temp.resize(count); + std::copy( + static_cast(dbr), + static_cast(dbr) + count, + temp.begin()); + value->replace(freeze(temp)); +} + +template +void get_DBRControl(const void * dbr, double *upper_ctrl_limit,double *lower_ctrl_limit) +{ + *upper_ctrl_limit = static_cast(dbr)->upper_ctrl_limit; + *lower_ctrl_limit = static_cast(dbr)->lower_ctrl_limit; +} + +template +void get_DBRDisplay( + const void * dbr, double *upper_disp_limit,double *lower_disp_limit,string *units) +{ + *upper_disp_limit = static_cast(dbr)->upper_disp_limit; + *lower_disp_limit = static_cast(dbr)->lower_disp_limit; + *units = static_cast(dbr)->units; +} + +template +void get_DBRValueAlarm( + const void * dbr, + double *upper_alarm_limit,double *upper_warning_limit, + double *lower_warning_limit,double *lower_alarm_limit) +{ + *upper_alarm_limit = static_cast(dbr)->upper_alarm_limit; + *upper_warning_limit = static_cast(dbr)->upper_warning_limit; + *lower_warning_limit = static_cast(dbr)->lower_warning_limit; + *lower_alarm_limit = static_cast(dbr)->lower_alarm_limit; +} + +Status DbdToPv::getFromDBD( + PVStructurePtr const & pvStructure, + BitSet::shared_pointer const & bitSet, + struct event_handler_args &args) +{ + if(args.status!=ECA_NORMAL) + { + Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); + return errorStatus; + } + if(fieldRequested) + { + void * value = dbr_value_ptr(args.dbr,caRequestType); + if(isArray) { + long count = args.count; + PVScalarArrayPtr pvValue = pvStructure->getSubField("value"); + switch(caValueType) { + case DBR_STRING: + { + const dbr_string_t *dbrval = static_cast(value); + PVStringArrayPtr pvValue = pvStructure->getSubField("value"); + PVStringArray::svector arr(pvValue->reuse()); + arr.resize(count); + std::copy(dbrval, dbrval + count, arr.begin()); + pvValue->replace(freeze(arr)); + break; + } + case DBR_CHAR: + copy_DBRScalarArray(value,count,pvValue); + break; + case DBR_SHORT: + copy_DBRScalarArray(value,count,pvValue); + break; + case DBR_LONG: + copy_DBRScalarArray(value,count,pvValue); + break; + case DBR_FLOAT: + copy_DBRScalarArray(value,count,pvValue); + break; + case DBR_DOUBLE: + copy_DBRScalarArray(value,count,pvValue); + break; + default: + Status errorStatus( + Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error")); + return errorStatus; + } + } else { + PVScalarPtr pvValue = pvStructure->getSubField("value"); + switch(caValueType) { + case DBR_ENUM: + { + const dbr_enum_t *dbrval = static_cast(value); + PVIntPtr value = pvStructure->getSubField("value.index"); + value->put(*dbrval); + PVStringArrayPtr pvChoices + = pvStructure->getSubField("value.choices"); + if(pvChoices->getLength()==0) + { + ConvertPtr convert = getConvert(); + size_t n = choices.size(); + pvChoices->setLength(n); + convert->fromStringArray(pvChoices,0,n,choices,0); + bitSet->set(pvStructure->getSubField("value")->getFieldOffset()); + } else { + bitSet->set(value->getFieldOffset()); + } + break; + } + case DBR_STRING: copy_DBRScalar(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_FLOAT: copy_DBRScalar(value,pvValue); break; + case DBR_DOUBLE: copy_DBRScalar(value,pvValue); break; + default: + Status errorStatus( + Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error")); + return errorStatus; + } + } + if(caValueType!=DBR_ENUM) { + bitSet->set(pvStructure->getSubField("value")->getFieldOffset()); + } + } + if(alarmRequested) { + // Note that status and severity are aways the first two members of DBR_ + const dbr_sts_string *data = static_cast(args.dbr); + dbr_short_t status = data->status; + dbr_short_t severity = data->severity; + bool statusChanged = false; + bool severityChanged = false; + PVStructurePtr pvAlarm(pvStructure->getSubField("alarm")); + PVIntPtr pvSeverity(pvAlarm->getSubField("severity")); + if(caAlarm.severity!=severity) { + caAlarm.severity = severity; + pvSeverity->put(severity); + severityChanged = true; + } + PVStringPtr pvMessage(pvAlarm->getSubField("message")); + PVIntPtr pvStatus(pvAlarm->getSubField("status")); + if(caAlarm.status!=status) { + caAlarm.status = status; + pvStatus->put(status); + string message("UNKNOWN STATUS"); + if(status<=ALARM_NSTATUS) message = string(epicsAlarmConditionStrings[status]); + pvMessage->put(message); + statusChanged = true; + } + if(statusChanged&&severityChanged) { + bitSet->set(pvAlarm->getFieldOffset()); + } else if(severityChanged) { + bitSet->set(pvSeverity->getFieldOffset()); + } else if(statusChanged) { + bitSet->set(pvStatus->getFieldOffset()); + bitSet->set(pvMessage->getFieldOffset()); + } + } + if(timeStampRequested) { + // Note that epicsTimeStamp always follows status and severity + const dbr_time_string *data = static_cast(args.dbr); + epicsTimeStamp stamp = data->stamp; + PVStructurePtr pvTimeStamp(pvStructure->getSubField("timeStamp")); + if(caTimeStamp.secPastEpoch!=stamp.secPastEpoch) { + caTimeStamp.secPastEpoch = stamp.secPastEpoch; + PVLongPtr pvSeconds(pvTimeStamp->getSubField("secondsPastEpoch")); + pvSeconds->put(stamp.secPastEpoch+posixEpochAtEpicsEpoch); + bitSet->set(pvSeconds->getFieldOffset()); + } + if(caTimeStamp.nsec!=stamp.nsec) { + caTimeStamp.secPastEpoch = stamp.secPastEpoch; + PVIntPtr pvNano(pvTimeStamp->getSubField("nanoseconds")); + pvNano->put(stamp.nsec); + bitSet->set(pvNano->getFieldOffset()); + } + } + if(controlRequested) + { + double upper_ctrl_limit = 0.0; + double lower_ctrl_limit = 0.0; + switch(caRequestType) { + case DBR_CTRL_CHAR: + get_DBRControl(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break; + case DBR_CTRL_SHORT: + get_DBRControl(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break; + case DBR_CTRL_LONG: + get_DBRControl(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break; + case DBR_CTRL_FLOAT: + get_DBRControl(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break; + case DBR_CTRL_DOUBLE: + get_DBRControl(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break; + default : + throw std::runtime_error("DbdToPv::getFromDBD logic error"); + } + PVStructurePtr pvControl(pvStructure->getSubField("control")); + if(caControl.upper_ctrl_limit!=upper_ctrl_limit) { + caControl.upper_ctrl_limit = upper_ctrl_limit; + PVDoublePtr pv = pvControl->getSubField("limitHigh"); + pv->put(upper_ctrl_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caControl.lower_ctrl_limit!=lower_ctrl_limit) { + caControl.lower_ctrl_limit = lower_ctrl_limit; + PVDoublePtr pv = pvControl->getSubField("limitLow"); + pv->put(lower_ctrl_limit); + bitSet->set(pv->getFieldOffset()); + } + } + if(displayRequested) + { + string units; + string format; + double upper_disp_limit = 0.0; + double lower_disp_limit = 0.0; + switch(caRequestType) { + case DBR_CTRL_CHAR: + get_DBRDisplay(args.dbr,&upper_disp_limit,&lower_disp_limit,&units); + format = "I4"; break; + case DBR_CTRL_SHORT: + get_DBRDisplay(args.dbr,&upper_disp_limit,&lower_disp_limit,&units); + format = "I6"; break; + case DBR_CTRL_LONG: + get_DBRDisplay(args.dbr,&upper_disp_limit,&lower_disp_limit,&units); + format = "I12"; break; + case DBR_CTRL_FLOAT: + get_DBRDisplay(args.dbr,&upper_disp_limit,&lower_disp_limit,&units); + { + const dbr_ctrl_float *data = static_cast(args.dbr); + int prec = data->precision; + ostringstream s; + s << "F" << prec + 6 << "." << prec; + format = s.str(); + } + break; + case DBR_CTRL_DOUBLE: + get_DBRDisplay(args.dbr,&upper_disp_limit,&lower_disp_limit,&units); + { + const dbr_ctrl_double *data = static_cast(args.dbr); + int prec = data->precision; + ostringstream s; + s << "F" << prec + 6 << "." << prec; + format = s.str(); + } + break; + default : + throw std::runtime_error("DbdToPv::getFromDBD logic error"); + } + PVStructurePtr pvDisplay(pvStructure->getSubField("display")); + if(caDisplay.lower_disp_limit!=lower_disp_limit) { + caDisplay.lower_disp_limit = lower_disp_limit; + PVDoublePtr pvDouble = pvDisplay->getSubField("limitLow"); + pvDouble->put(lower_disp_limit); + bitSet->set(pvDouble->getFieldOffset()); + } + if(caDisplay.upper_disp_limit!=upper_disp_limit) { + caDisplay.upper_disp_limit = upper_disp_limit; + PVDoublePtr pvDouble = pvDisplay->getSubField("limitHigh"); + pvDouble->put(upper_disp_limit); + bitSet->set(pvDouble->getFieldOffset()); + } + if(caDisplay.units!=units) { + caDisplay.units = units; + PVStringPtr pvString = pvDisplay->getSubField("units"); + pvString->put(units); + bitSet->set(pvString->getFieldOffset()); + } + if(caDisplay.format!=format) { + caDisplay.format = format; + PVStringPtr pvString = pvDisplay->getSubField("format"); + 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; + double upper_warning_limit = 0.0; + double lower_warning_limit = 0.0; + double lower_alarm_limit = 0.0; + switch(caRequestType) { + case DBR_CTRL_CHAR: + get_DBRValueAlarm(args.dbr, + &upper_alarm_limit,&upper_warning_limit, + &lower_warning_limit,&lower_alarm_limit); + break; + case DBR_CTRL_SHORT: + get_DBRValueAlarm(args.dbr, + &upper_alarm_limit,&upper_warning_limit, + &lower_warning_limit,&lower_alarm_limit); + break; + case DBR_CTRL_LONG: + get_DBRValueAlarm(args.dbr, + &upper_alarm_limit,&upper_warning_limit, + &lower_warning_limit,&lower_alarm_limit); + break; + case DBR_CTRL_FLOAT: + get_DBRValueAlarm(args.dbr, + &upper_alarm_limit,&upper_warning_limit, + &lower_warning_limit,&lower_alarm_limit); + break; + case DBR_CTRL_DOUBLE: + get_DBRValueAlarm(args.dbr, + &upper_alarm_limit,&upper_warning_limit, + &lower_warning_limit,&lower_alarm_limit); + break; + default : + throw std::runtime_error("DbdToPv::getFromDBD logic error"); + } + ConvertPtr convert(getConvert()); + PVStructurePtr pvValueAlarm(pvStructure->getSubField("valueAlarm")); + if(caValueAlarm.upper_alarm_limit!=upper_alarm_limit) { + caValueAlarm.upper_alarm_limit = upper_alarm_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("highAlarmLimit"); + convert->fromDouble(pv,upper_alarm_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caValueAlarm.upper_warning_limit!=upper_warning_limit) { + caValueAlarm.upper_warning_limit = upper_warning_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("highWarningLimit"); + convert->fromDouble(pv,upper_warning_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caValueAlarm.lower_warning_limit!=lower_warning_limit) { + caValueAlarm.lower_warning_limit = lower_warning_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("lowWarningLimit"); + convert->fromDouble(pv,lower_warning_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caValueAlarm.lower_alarm_limit!=lower_alarm_limit) { + caValueAlarm.lower_alarm_limit = lower_alarm_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("lowAlarmLimit"); + convert->fromDouble(pv,lower_alarm_limit); + bitSet->set(pv->getFieldOffset()); + } + } + if(firstTime) { + firstTime = false; + bitSet->clear(); + bitSet->set(0); + } + return Status::Ok; +} + +template +const void * put_DBRScalar(dbrT *val,PVScalar::shared_pointer const & pvScalar) +{ + std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvScalar); + *val = value->get(); + return val; +} + +template +const void * put_DBRScalarArray(unsigned long*count, PVScalarArray::shared_pointer const & pvArray) +{ + std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvArray); + *count = value->getLength(); + return value->view().data(); +} + + +Status DbdToPv::putToDBD( + CAChannelPtr const & caChannel, + PVStructurePtr const & pvStructure, + bool block, + caCallbackFunc putHandler, + void * userarg) +{ + chid channelID = caChannel->getChannelID(); + const void *pValue = NULL; + unsigned long count = 1; + char *ca_stringBuffer(0); + dbr_char_t bvalue(0); + dbr_short_t svalue(0); + dbr_long_t lvalue(0); + dbr_float_t fvalue(0); + dbr_double_t dvalue(0); + if(isArray) { + PVScalarArrayPtr pvValue = pvStructure->getSubField("value"); + switch(caValueType) { + case DBR_STRING: + { + PVStringArrayPtr pvValue = pvStructure->getSubField("value"); + count = pvValue->getLength(); + if(count<1) break; + if(count>maxElements) count = maxElements; + int nbytes = count*MAX_STRING_SIZE; + ca_stringBuffer = new char[nbytes]; + memset(ca_stringBuffer, 0, nbytes); + pValue = ca_stringBuffer; + PVStringArray::const_svector stringArray(pvValue->view()); + char *pnext = ca_stringBuffer; + for(size_t i=0; i= MAX_STRING_SIZE) len = MAX_STRING_SIZE - 1; + memcpy(pnext, value.c_str(), len); + pnext += MAX_STRING_SIZE; + } + break; + } + case DBR_CHAR: + pValue = put_DBRScalarArray(&count,pvValue); + break; + case DBR_SHORT: + pValue = put_DBRScalarArray(&count,pvValue); + break; + case DBR_LONG: + pValue = put_DBRScalarArray(&count,pvValue); + break; + case DBR_FLOAT: + pValue = put_DBRScalarArray(&count,pvValue); + break; + case DBR_DOUBLE: + pValue = put_DBRScalarArray(&count,pvValue); + break; + default: + Status errorStatus( + Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error")); + return errorStatus; + } + } else { + PVScalarPtr pvValue = pvStructure->getSubField("value"); + switch(caValueType) { + case DBR_ENUM: + { + bool wait = false; + { + Lock lock(choicesMutex); + if(!choicesValid) { + wait = true; + waitForChoicesValid = true; + } + } + bool result = true; + if(wait) { + result = choicesEvent.wait(5.0); + } + if(!result) { + Status errorStatus( + Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD ")); + return errorStatus; + } + dbr_enum_t indexvalue = pvStructure->getSubField("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_FLOAT: pValue = put_DBRScalar(&fvalue,pvValue); break; + case DBR_DOUBLE: pValue = put_DBRScalar(&dvalue,pvValue); break; + default: + Status errorStatus( + Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error")); + return errorStatus; + } + } + Status status = Status::Ok; + int result = 0; + caChannel->attachContext(); + if(block) { + result = ca_array_put_callback(caValueType,count,channelID,pValue,putHandler,userarg); + } else { + result = ca_array_put(caValueType,count,channelID,pValue); + } + if(result==ECA_NORMAL) { + ca_flush_io(); + } else { + status = Status(Status::STATUSTYPE_ERROR, string(ca_message(result))); + } + if(ca_stringBuffer!=NULL) delete[] ca_stringBuffer; + return status; +} + +}}} diff --git a/src/ca/dbdToPv.h b/src/ca/dbdToPv.h new file mode 100644 index 00000000..5ee77b59 --- /dev/null +++ b/src/ca/dbdToPv.h @@ -0,0 +1,145 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvAccessCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/** + * @author mrk + * @date 2018.03 + */ +#ifndef DbdToPv_H +#define DbdToPv_H + +#include +#include +#include +#include +#include "caChannel.h" + +namespace epics { +namespace pvAccess { +namespace ca { + +enum IOType {getIO,putIO,monitorIO}; + +class AlarmDbd; +typedef std::tr1::shared_ptr AlarmDbdPtr; +class TimeStampDbd; +typedef std::tr1::shared_ptr TimeStampDbdPtr; +class DisplayDbd; +typedef std::tr1::shared_ptr DisplayDbdPtr; +class ControlDbd; +typedef std::tr1::shared_ptr ControlDbdPtr; +class ValueAlarmDbd; +typedef std::tr1::shared_ptr ValueAlarmDbdPtr; + +struct CaAlarm +{ + dbr_short_t status; + dbr_short_t severity; + CaAlarm() : status(0), severity(0) {} +}; + +struct CaDisplay +{ + double lower_disp_limit; + double upper_disp_limit; + std::string units; + std::string format; + CaDisplay() : lower_disp_limit(0),upper_disp_limit(0) {} +}; + +struct CaControl +{ + double upper_ctrl_limit; + double lower_ctrl_limit; + CaControl() : upper_ctrl_limit(0),lower_ctrl_limit(0) {} +}; + +struct CaValueAlarm +{ + double upper_alarm_limit; + double upper_warning_limit; + double lower_warning_limit; + double lower_alarm_limit; + CaValueAlarm() : + upper_alarm_limit(0), + upper_warning_limit(0), + lower_warning_limit(0), + lower_alarm_limit(0) + {} +}; + +class DbdToPv; +typedef std::tr1::shared_ptr DbdToPvPtr; + +typedef void ( caCallbackFunc ) (struct event_handler_args); + +/** + * @brief DbdToPv converts between DBD data and pvData. + * + * + */ +class DbdToPv +{ +public: + POINTER_DEFINITIONS(DbdToPv); + static DbdToPvPtr create( + CAChannelPtr const & caChannel, + epics::pvData::PVStructurePtr const & pvRequest, + IOType ioType + ); + epics::pvData::PVStructurePtr createPVStructure(); + chtype getRequestType(); + epics::pvData::Status getFromDBD( + epics::pvData::PVStructurePtr const & pvStructure, + epics::pvData::BitSet::shared_pointer const & bitSet, + struct event_handler_args &args + ); + epics::pvData::Status putToDBD( + CAChannelPtr const & caChannel, + epics::pvData::PVStructurePtr const & pvStructure, + bool block, + caCallbackFunc putHandler, + void *userArg + ); + void getChoicesDone(struct event_handler_args &args); + void descriptionConnected(struct connection_handler_args args); + void getDescriptionDone(struct event_handler_args &args); +private: + DbdToPv(IOType ioType); + void activate( + CAChannelPtr const & caChannel, + epics::pvData::PVStructurePtr const & pvRequest + ); + IOType ioType; + bool fieldRequested; + bool alarmRequested; + bool timeStampRequested; + bool displayRequested; + bool controlRequested; + bool valueAlarmRequested; + bool isArray; + bool firstTime; + bool choicesValid; + bool waitForChoicesValid; + chtype caValueType; + chtype caRequestType; + unsigned long maxElements; + epics::pvData::Mutex choicesMutex; + epics::pvData::Event choicesEvent; + epicsTimeStamp caTimeStamp; + CaAlarm caAlarm; + CaDisplay caDisplay; + CaControl caControl; + CaValueAlarm caValueAlarm; + std::string description; + epics::pvData::Structure::const_shared_pointer structure; + std::vector choices; +}; + +} +} +} + +#endif /* DbdToPv_H */ diff --git a/src/ca/getDoneThread.cpp b/src/ca/getDoneThread.cpp new file mode 100644 index 00000000..ae01f1aa --- /dev/null +++ b/src/ca/getDoneThread.cpp @@ -0,0 +1,114 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvAccessCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/** + * @author mrk + * @date 2018.07 + */ + +#include "caChannel.h" +#include +#define epicsExportSharedSymbols +#include "getDoneThread.h" + +using namespace epics::pvData; +using namespace std; + +namespace epics { +namespace pvAccess { +namespace ca { + +GetDoneThreadPtr GetDoneThread::get() +{ + static GetDoneThreadPtr master; + static Mutex mutex; + Lock xx(mutex); + if(!master) { + master = GetDoneThreadPtr(new GetDoneThread()); + master->start(); + } + return master; +} + +GetDoneThread::GetDoneThread() +: isStop(false) +{ +} + +GetDoneThread::~GetDoneThread() +{ +//std::cout << "GetDoneThread::~GetDoneThread()\n"; +} + + +void GetDoneThread::start() +{ + thread = std::tr1::shared_ptr(new epicsThread( + *this, + "getDoneThread", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +} + + +void GetDoneThread::stop() +{ + { + Lock xx(mutex); + isStop = true; + } + waitForCommand.signal(); + waitForStop.wait(); +} + +void GetDoneThread::getDone(NotifyGetRequesterPtr const ¬ifyGetRequester) +{ + { + Lock lock(mutex); + if(notifyGetRequester->isOnQueue) return; + notifyGetRequester->isOnQueue = true; + notifyGetQueue.push(notifyGetRequester); + } + waitForCommand.signal(); +} + +void GetDoneThread::run() +{ + while(true) + { + waitForCommand.wait(); + while(true) { + bool more = false; + NotifyGetRequester* notifyGetRequester(NULL); + { + Lock lock(mutex); + if(!notifyGetQueue.empty()) + { + more = true; + NotifyGetRequesterWPtr req(notifyGetQueue.front()); + notifyGetQueue.pop(); + NotifyGetRequesterPtr reqPtr(req.lock()); + if(reqPtr) { + notifyGetRequester = reqPtr.get(); + reqPtr->isOnQueue = false; + } + } + } + if(!more) break; + if(notifyGetRequester!=NULL) + { + CAChannelGetPtr channelGet(notifyGetRequester->channelGet.lock()); + if(channelGet) channelGet->notifyClient(); + } + } + if(isStop) { + waitForStop.signal(); + break; + } + } +} + +}}} diff --git a/src/ca/getDoneThread.h b/src/ca/getDoneThread.h new file mode 100644 index 00000000..6522f0ae --- /dev/null +++ b/src/ca/getDoneThread.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 GetDoneThread_H +#define GetDoneThread_H +#include +#include +#include +#include +#include +#include + +namespace epics { +namespace pvAccess { +namespace ca { + +class NotifyGetRequester; +typedef std::tr1::shared_ptr NotifyGetRequesterPtr; +typedef std::tr1::weak_ptr NotifyGetRequesterWPtr; + + +class GetDoneThread; +typedef std::tr1::shared_ptr GetDoneThreadPtr; + +class CAChannelGet; +typedef std::tr1::shared_ptr CAChannelGetPtr; +typedef std::tr1::weak_ptr CAChannelGetWPtr; + +class NotifyGetRequester +{ +public: + ChannelGetRequester::weak_pointer channelGetRequester; + CAChannelGetWPtr channelGet; + bool isOnQueue; + NotifyGetRequester() : isOnQueue(false) {} + void setChannelGet(CAChannelGetPtr const &channelGet) + { this->channelGet = channelGet;} +}; + + +class GetDoneThread : + public epicsThreadRunable +{ +public: + static GetDoneThreadPtr get(); + ~GetDoneThread(); + virtual void run(); + void start(); + void stop(); + void getDone(NotifyGetRequesterPtr const ¬ifyGetRequester); +private: + GetDoneThread(); + + bool isStop; + std::tr1::shared_ptr thread; + epics::pvData::Mutex mutex; + epics::pvData::Event waitForCommand; + epics::pvData::Event waitForStop; + std::queue notifyGetQueue; +}; + + +}}} + +#endif /* GetDoneThread_H */ diff --git a/src/ca/monitorEventThread.cpp b/src/ca/monitorEventThread.cpp new file mode 100644 index 00000000..d74cbd13 --- /dev/null +++ b/src/ca/monitorEventThread.cpp @@ -0,0 +1,113 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvAccessCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/** + * @author mrk + * @date 2018.06 + */ + +#include "caChannel.h" +#include +#define epicsExportSharedSymbols +#include "monitorEventThread.h" + +using namespace epics::pvData; +using namespace std; + +namespace epics { +namespace pvAccess { +namespace ca { + +MonitorEventThreadPtr MonitorEventThread::get() +{ + static MonitorEventThreadPtr master; + static Mutex mutex; + Lock xx(mutex); + if(!master) { + master = MonitorEventThreadPtr(new MonitorEventThread()); + master->start(); + } + return master; +} + +MonitorEventThread::MonitorEventThread() +: isStop(false) +{ +} + +MonitorEventThread::~MonitorEventThread() +{ +//std::cout << "MonitorEventThread::~MonitorEventThread()\n"; +} + +void MonitorEventThread::start() +{ + thread = std::tr1::shared_ptr(new epicsThread( + *this, + "monitorEventThread", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +} + +void MonitorEventThread::stop() +{ + { + Lock xx(mutex); + isStop = true; + } + waitForCommand.signal(); + waitForStop.wait(); +} + + +void MonitorEventThread::event(NotifyMonitorRequesterPtr const ¬ifyMonitorRequester) +{ + { + Lock lock(mutex); + if(notifyMonitorRequester->isOnQueue) return; + notifyMonitorRequester->isOnQueue = true; + notifyMonitorQueue.push(notifyMonitorRequester); + } + waitForCommand.signal(); +} + +void MonitorEventThread::run() +{ + while(true) + { + waitForCommand.wait(); + while(true) { + bool more = false; + NotifyMonitorRequester* notifyMonitorRequester(NULL); + { + Lock lock(mutex); + if(!notifyMonitorQueue.empty()) + { + more = true; + NotifyMonitorRequesterWPtr req(notifyMonitorQueue.front()); + notifyMonitorQueue.pop(); + NotifyMonitorRequesterPtr reqPtr(req.lock()); + if(reqPtr) { + notifyMonitorRequester = reqPtr.get(); + reqPtr->isOnQueue = false; + } + } + } + if(!more) break; + if(notifyMonitorRequester!=NULL) + { + CAChannelMonitorPtr channelMonitor(notifyMonitorRequester->channelMonitor.lock()); + if(channelMonitor) channelMonitor->notifyClient(); + } + } + if(isStop) { + waitForStop.signal(); + break; + } + } +} + +}}} diff --git a/src/ca/monitorEventThread.h b/src/ca/monitorEventThread.h new file mode 100644 index 00000000..0a03ee71 --- /dev/null +++ b/src/ca/monitorEventThread.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.06 + */ +#ifndef MonitorEventThread_H +#define MonitorEventThread_H +#include +#include +#include +#include +#include +#include + +namespace epics { +namespace pvAccess { +namespace ca { + +class NotifyMonitorRequester; +typedef std::tr1::shared_ptr NotifyMonitorRequesterPtr; +typedef std::tr1::weak_ptr NotifyMonitorRequesterWPtr; + + +class MonitorEventThread; +typedef std::tr1::shared_ptr MonitorEventThreadPtr; + +class CAChannelMonitor; +typedef std::tr1::shared_ptr CAChannelMonitorPtr; +typedef std::tr1::weak_ptr CAChannelMonitorWPtr; + +class NotifyMonitorRequester +{ +public: + MonitorRequester::weak_pointer monitorRequester; + CAChannelMonitorWPtr channelMonitor; + bool isOnQueue; + NotifyMonitorRequester() : isOnQueue(false) {} + void setChannelMonitor(CAChannelMonitorPtr const &channelMonitor) + { this->channelMonitor = channelMonitor;} +}; + + +class MonitorEventThread : + public epicsThreadRunable +{ +public: + static MonitorEventThreadPtr get(); + ~MonitorEventThread(); + virtual void run(); + void start(); + void stop(); + void event(NotifyMonitorRequesterPtr const ¬ifyMonitorRequester); +private: + MonitorEventThread(); + + bool isStop; + std::tr1::shared_ptr thread; + epics::pvData::Mutex mutex; + epics::pvData::Event waitForCommand; + epics::pvData::Event waitForStop; + std::queue notifyMonitorQueue; +}; + + +}}} + +#endif /* MonitorEventThread_H */ diff --git a/src/ca/putDoneThread.cpp b/src/ca/putDoneThread.cpp new file mode 100644 index 00000000..79eb6e9e --- /dev/null +++ b/src/ca/putDoneThread.cpp @@ -0,0 +1,114 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvAccessCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/** + * @author mrk + * @date 2018.07 + */ + +#include "caChannel.h" +#include +#define epicsExportSharedSymbols +#include "putDoneThread.h" + +using namespace epics::pvData; +using namespace std; + +namespace epics { +namespace pvAccess { +namespace ca { + +PutDoneThreadPtr PutDoneThread::get() +{ + static PutDoneThreadPtr master; + static Mutex mutex; + Lock xx(mutex); + if(!master) { + master = PutDoneThreadPtr(new PutDoneThread()); + master->start(); + } + return master; +} + +PutDoneThread::PutDoneThread() +: isStop(false) +{ +} + +PutDoneThread::~PutDoneThread() +{ +//std::cout << "PutDoneThread::~PutDoneThread()\n"; +} + + +void PutDoneThread::start() +{ + thread = std::tr1::shared_ptr(new epicsThread( + *this, + "putDoneThread", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +} + + +void PutDoneThread::stop() +{ + { + Lock xx(mutex); + isStop = true; + } + waitForCommand.signal(); + waitForStop.wait(); +} + +void PutDoneThread::putDone(NotifyPutRequesterPtr const ¬ifyPutRequester) +{ + { + Lock lock(mutex); + if(notifyPutRequester->isOnQueue) return; + notifyPutRequester->isOnQueue = true; + notifyPutQueue.push(notifyPutRequester); + } + waitForCommand.signal(); +} + +void PutDoneThread::run() +{ + while(true) + { + waitForCommand.wait(); + while(true) { + bool more = false; + NotifyPutRequester* notifyPutRequester(NULL); + { + Lock lock(mutex); + if(!notifyPutQueue.empty()) + { + more = true; + NotifyPutRequesterWPtr req(notifyPutQueue.front()); + notifyPutQueue.pop(); + NotifyPutRequesterPtr reqPtr(req.lock()); + if(reqPtr) { + notifyPutRequester = reqPtr.get(); + reqPtr->isOnQueue = false; + } + } + } + if(!more) break; + if(notifyPutRequester!=NULL) + { + CAChannelPutPtr channelPut(notifyPutRequester->channelPut.lock()); + if(channelPut) channelPut->notifyClient(); + } + } + if(isStop) { + waitForStop.signal(); + break; + } + } +} + +}}} diff --git a/src/ca/putDoneThread.h b/src/ca/putDoneThread.h new file mode 100644 index 00000000..bab0f21b --- /dev/null +++ b/src/ca/putDoneThread.h @@ -0,0 +1,70 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvAccessCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/** + * @author mrk + * @date 2018.07 + */ +#ifndef PutDoneThread_H +#define PutDoneThread_H +#include +#include +#include +#include +#include +#include + +namespace epics { +namespace pvAccess { +namespace ca { + +class NotifyPutRequester; +typedef std::tr1::shared_ptr NotifyPutRequesterPtr; +typedef std::tr1::weak_ptr NotifyPutRequesterWPtr; + + +class PutDoneThread; +typedef std::tr1::shared_ptr PutDoneThreadPtr; + +class CAChannelPut; +typedef std::tr1::shared_ptr CAChannelPutPtr; +typedef std::tr1::weak_ptr CAChannelPutWPtr; + +class NotifyPutRequester +{ +public: + ChannelPutRequester::weak_pointer channelPutRequester; + CAChannelPutWPtr channelPut; + bool isOnQueue; + NotifyPutRequester() : isOnQueue(false) {} + void setChannelPut(CAChannelPutPtr const &channelPut) + { this->channelPut = channelPut;} +}; + + +class PutDoneThread : + public epicsThreadRunable +{ +public: + static PutDoneThreadPtr get(); + ~PutDoneThread(); + virtual void run(); + void start(); + void stop(); + void putDone(NotifyPutRequesterPtr const ¬ifyPutRequester); +private: + PutDoneThread(); + bool isStop; + std::tr1::shared_ptr thread; + epics::pvData::Mutex mutex; + epics::pvData::Event waitForCommand; + epics::pvData::Event waitForStop; + std::queue notifyPutQueue; +}; + + +}}} + +#endif /* PutDoneThread_H */ diff --git a/src/ca/pv/caProvider.h b/src/ca/pv/caProvider.h index 3f75c4a4..90b880e2 100644 --- a/src/ca/pv/caProvider.h +++ b/src/ca/pv/caProvider.h @@ -3,6 +3,9 @@ * pvAccessCPP is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ +/** + * @author msekoranja + */ #ifndef CAPROVIDER_H #define CAPROVIDER_H @@ -10,6 +13,8 @@ #include #include +struct ca_client_context; + namespace epics { namespace pvAccess { namespace ca { @@ -17,23 +22,36 @@ namespace ca { /** * @brief CAClientFactory is a channel provider for the ca network provider. * + * A single instance is created the first time CAClientFactory::start is called. + * epicsAtExit is used to destroy the instance. + * + * The single instance calls: + * ca_context_create(ca_enable_preemptive_callback); + * + * The thread that calls start, or a ca auxillary thread, are the only threads + * that can call the ca_* functions. + * + * NOTE: callbacks for monitor, get, and put are made from a separate thread. + * This is done to prevent a deadly embrace that can occur + * when rapid gets, puts, and monitor events are happening. + * The callbacks should not call any pvAccess method. + * If any such call is made the separate thread becomes a ca auxillary thread. * */ class epicsShareClass CAClientFactory { public: - /** @brief start the provider + /** @brief start provider ca * */ static void start(); - /** @brief stop the provider + /** @brief stop provider ca * + * This does nothing since epicsAtExit is used to destroy the instance. */ static void stop(); }; -} -} -} +}}} #endif /* CAPROVIDER_H */ diff --git a/src/ca/pv/caStatus.h b/src/ca/pv/caStatus.h deleted file mode 100644 index 33ba6f3e..00000000 --- a/src/ca/pv/caStatus.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * pvAccessCPP is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ - -#ifndef CASTATUS_H -#define CASTATUS_H - -#include -#include - -namespace epics { -namespace pvAccess { -namespace ca { - -enum AlarmStatus { - noStatus,deviceStatus,driverStatus,recordStatus, - dbStatus,confStatus,undefinedStatus,clientStatus -}; - -epicsShareExtern std::string dbrStatus2alarmMessage[]; -epicsShareExtern int dbrStatus2alarmStatus[]; - -} -} -} - -#endif /* CASTATUS_H */ diff --git a/testCa/Makefile b/testCa/Makefile new file mode 100644 index 00000000..9cfe3174 --- /dev/null +++ b/testCa/Makefile @@ -0,0 +1,47 @@ +# Makefile for the ca tests + +TOP = .. +include $(TOP)/configure/CONFIG + +# Need access to caProviderPvt.h +USR_CPPFLAGS += -I$(TOP)/src/ca + +PROD_LIBS += pvAccess pvAccessCA pvData $(EPICS_BASE_IOC_LIBS) + +TESTPROD_HOST += testCaProvider +testCaProvider_SRCS += testCaProvider.cpp +TESTS += testCaProvider +ifdef BASE_3_16 + testCaProvider_SRCS += testIoc_registerRecordDeviceDriver.cpp + REGRDDFLAGS = -l +endif +caTestHarness_SRCS += $(testCaProvider_SRCS) + +# Ensure EPICS_HOST_ARCH is set in the environment +export EPICS_HOST_ARCH + +ifdef BASE_3_16 + # Embedded OSes need Base-3.16.2 or higher to pass tests + + # Code that runs all tests in the collection + caTestHarness_SRCS += pvCaAllTests.c + + # Build for vxWorks + PROD_vxWorks = caTestHarness + TESTSPEC_vxWorks = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests + + # Build for RTEMS, with harness code & configuration + PROD_RTEMS += caTestHarness + caTestHarness_SRCS_RTEMS += rtemsTestHarness.c + TESTSPEC_RTEMS = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests +endif + +# Build test scripts for hosts +TESTSCRIPTS_HOST += $(TESTS:%=%.t) + +include $(TOP)/configure/RULES + +ifdef BASE_3_16 + $(COMMON_DIR)/testIoc.dbd: $(EPICS_BASE)/dbd/softIoc.dbd + $(CP) $< $@ +endif diff --git a/testCa/pvCaAllTests.c b/testCa/pvCaAllTests.c new file mode 100644 index 00000000..43d37059 --- /dev/null +++ b/testCa/pvCaAllTests.c @@ -0,0 +1,18 @@ +/* + * + */ + +#include +#include +#include +#include + +int testCaProvider(void); + +void pvCaAllTests(void) +{ + testHarness(); + runTest(testCaProvider); + + epicsExit(0); /* Trigger test harness */ +} diff --git a/testCa/qemuRunTest.sh b/testCa/qemuRunTest.sh new file mode 100755 index 00000000..b9cd499f --- /dev/null +++ b/testCa/qemuRunTest.sh @@ -0,0 +1 @@ +qemu-system-i386 --kernel O.RTEMS-pc386/rtemsTestHarness -netdev user,id=mynet0 -device ne2k_isa,netdev=mynet0 -redir tcp:5075::5075 -redir udp:5076::5076 -m 1024 --no-reboot -curses diff --git a/testCa/rtemsTestHarness.c b/testCa/rtemsTestHarness.c new file mode 100644 index 00000000..9c09d22c --- /dev/null +++ b/testCa/rtemsTestHarness.c @@ -0,0 +1,12 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ + +extern void pvCaAllTests(void); + +int main(int argc, char **argv) +{ + pvCaAllTests(); /* calls epicsExit(0) */ + return 0; +} diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp new file mode 100644 index 00000000..5a1664f3 --- /dev/null +++ b/testCa/testCaProvider.cpp @@ -0,0 +1,802 @@ +/* testCaProvider.cpp */ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ +/* Author: Marty Kraimer Date: 2018.05 */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef EPICS_VERSION_INT + #if EPICS_VERSION_INT >= VERSION_INT(3,16,2,0) + #define USE_DBUNITTEST + // USE_TYPED_RSET prevents deprecation warnings + #define USE_TYPED_RSET + #define EXIT_TESTS 0 + #include + #include + #include + + extern "C" int testIoc_registerRecordDeviceDriver(struct dbBase *pbase); + #endif +#endif + +#ifndef EXIT_TESTS + #define EXIT_TESTS 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// DEBUG must be 0 to run under the automated test harness +#define DEBUG 0 + +using namespace epics::pvData; +using namespace epics::pvAccess; +using namespace epics::pvAccess::ca; +using namespace std; + +class TestChannel; +typedef std::tr1::shared_ptr TestChannelPtr; + +class TestChannel: + public ChannelRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannel); + string getRequesterName(); + void message( + string const & message, + MessageType messageType); + virtual void channelCreated(const Status& status, Channel::shared_pointer const & channel); + virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState); + string getChannelName(); + Channel::shared_pointer getChannel(); + static TestChannelPtr create(string const & channelName); + void connect(); + void waitConnect(double timeout); +private: + TestChannel(string const & channelName); + string channelName; + Event waitForConnect; + Channel::shared_pointer channel; +}; + +string TestChannel::getChannelName() { return channelName;} + +Channel::shared_pointer TestChannel::getChannel() { return channel;} + +TestChannelPtr TestChannel::create(string const & channelName) +{ + TestChannelPtr testChannel(new TestChannel(channelName)); + testChannel->connect(); + return testChannel; +} + +TestChannel::TestChannel(string const & channelName) +: channelName(channelName) +{ +} + +string TestChannel::getRequesterName() { return "testChannel";} +void TestChannel::message(string const & message,MessageType messageType) {}; + +void TestChannel::channelCreated(const Status& status, Channel::shared_pointer const & channel) +{ + if(channel->isConnected()) waitForConnect.signal(); +} + +void TestChannel::channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) +{ + if(connectionState==Channel::CONNECTED) waitForConnect.signal(); +} + +void TestChannel::connect() +{ + ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); + ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); + if(!channelProvider) throw std::runtime_error(channelName + " provider ca not registered"); + channel = channelProvider->createChannel(channelName,shared_from_this(),ChannelProvider::PRIORITY_DEFAULT); + if(!channel) throw std::runtime_error(channelName + " channelCreate failed "); + waitConnect(5.0); +} + +void TestChannel::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) return; + throw std::runtime_error(channelName + " TestChannel::waitConnect failed "); +} + +class TestChannelGet; +typedef std::tr1::shared_ptr TestChannelGetPtr; + +class TestChannelGetRequester; +typedef std::tr1::shared_ptr TestChannelGetRequesterPtr; +typedef std::tr1::weak_ptr TestChannelGetRequesterWPtr; + +class TestChannelGetRequester +{ +public: + virtual void getDone( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) = 0; +}; + +class TestChannelGet: + public ChannelGetRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannelGet); + virtual string getRequesterName(); + virtual void message(string const & message, epics::pvData::MessageType messageType) {} + virtual void channelGetConnect( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + Structure::const_shared_pointer const & structure); + virtual void getDone( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + static TestChannelGetPtr create( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + void connect(); + void waitConnect(double timeout); + void get(); +private: + TestChannelGet( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + + TestChannelGetRequesterWPtr getRequester; + TestChannelPtr testChannel; + PVStructurePtr pvRequest; + PVStructurePtr pvStructure; + Event waitForConnect; + ChannelGet::shared_pointer channelGet; +}; + +TestChannelGetPtr TestChannelGet::create( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) +{ + TestChannelGetPtr testChannelGet(new TestChannelGet(getRequester,testChannel,pvRequest)); + testChannelGet->connect(); + testChannelGet->waitConnect(5.0); + return testChannelGet; +} + +TestChannelGet::TestChannelGet( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) + : getRequester(getRequester), + testChannel(testChannel), + pvRequest(pvRequest) +{ +} + +string TestChannelGet::getRequesterName() {return "TestChannelGet";} + +void TestChannelGet::channelGetConnect( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + Structure::const_shared_pointer const & structure) +{ + waitForConnect.signal(); +} + +void TestChannelGet::getDone( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + TestChannelGetRequesterPtr req(getRequester.lock()); + if(!req) return; + if(status.isOK()) { + req->getDone(pvStructure,bitSet); + return; + } + string message = string("channel ") + + testChannel->getChannelName() + + " TestChannelGet::getDone " + + status.getMessage(); + throw std::runtime_error(message); +} + +void TestChannelGet::connect() +{ + channelGet = testChannel->getChannel()->createChannelGet(shared_from_this(),pvRequest); + if(!channelGet) throw std::runtime_error(testChannel->getChannelName() + " channelCreate failed "); +} + +void TestChannelGet::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) return; + throw std::runtime_error(testChannel->getChannelName() + " TestChannelGet::waitConnect failed "); +} + + +void TestChannelGet::get() +{ + channelGet->get(); +} + +class TestChannelPut; +typedef std::tr1::shared_ptr TestChannelPutPtr; + +class TestChannelPutRequester; +typedef std::tr1::shared_ptr TestChannelPutRequesterPtr; +typedef std::tr1::weak_ptr TestChannelPutRequesterWPtr; + +class TestChannelPutRequester +{ +public: + virtual void putDone() = 0; +}; + +class TestChannelPut: + public ChannelPutRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannelPut); + virtual string getRequesterName(); + virtual void message(string const & message, MessageType messageType) {} + virtual void channelPutConnect( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + Structure::const_shared_pointer const & structure); + virtual void putDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut); + virtual void getDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + static TestChannelPutPtr create( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel); + void connect(); + void waitConnect(double timeout); + void put(string const & value); +private: + TestChannelPut( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel); + + TestChannelPutRequesterWPtr putRequester; + TestChannelPtr testChannel; + PVStructurePtr pvStructure; + BitSetPtr bitSet; + Event waitForConnect; + ChannelPut::shared_pointer channelPut; +}; + +TestChannelPutPtr TestChannelPut::create( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel) +{ + TestChannelPutPtr testChannelPut(new TestChannelPut(putRequester,testChannel)); + testChannelPut->connect(); + testChannelPut->waitConnect(5.0); + return testChannelPut; +} + +TestChannelPut::TestChannelPut( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel) + : putRequester(putRequester), + testChannel(testChannel) +{ +} + +string TestChannelPut::getRequesterName() {return "TestChannelPut";} + +void TestChannelPut::channelPutConnect( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + Structure::const_shared_pointer const & structure) +{ + pvStructure = PVDataCreate::getPVDataCreate()->createPVStructure(structure); + bitSet = BitSetPtr(new BitSet(pvStructure->getNumberFields())); + waitForConnect.signal(); +} + +void TestChannelPut::getDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + throw std::runtime_error("TestChannelPut::getDone should not be called"); +} + +void TestChannelPut::putDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut) +{ + TestChannelPutRequesterPtr req(putRequester.lock()); + if(!req) return; + if(status.isOK()) { + req->putDone(); + return; + } + string message = string("channel ") + + testChannel->getChannelName() + + " TestChannelPut::putDone " + + status.getMessage(); + throw std::runtime_error(message); +} + +void TestChannelPut::connect() +{ + string request("value"); + PVStructurePtr pvRequest(createRequest(request)); + + channelPut = testChannel->getChannel()->createChannelPut(shared_from_this(),pvRequest); + if(!channelPut) throw std::runtime_error(testChannel->getChannelName() + " channelCreate failed "); +} + +void TestChannelPut::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) return; + throw std::runtime_error(testChannel->getChannelName() + + " TestChannelPut::waitConnect failed "); +} + + +void TestChannelPut::put(string const & value) +{ + PVFieldPtr pvField(pvStructure->getSubField("value")); + if(!pvField) throw std::runtime_error(testChannel->getChannelName() + + " TestChannelPut::put no value "); + FieldConstPtr field(pvField->getField()); + Type type(field->getType()); + if(type==scalar) { + PVScalarPtr pvScalar(std::tr1::static_pointer_cast(pvField)); + getConvert()->fromString(pvScalar,value); + bitSet->set(pvField->getFieldOffset()); + channelPut->put(pvStructure,bitSet); + return; + } + if(type==scalarArray) { + PVScalarArrayPtr pvScalarArray(std::tr1::static_pointer_cast(pvField)); + std::vector values; + size_t pos = 0; + size_t n = 1; + while(true) + { + size_t offset = value.find(" ",pos); + if(offset==string::npos) { + values.push_back(value.substr(pos)); + break; + } + values.push_back(value.substr(pos,offset-pos)); + pos = offset+1; + n++; + } + pvScalarArray->setLength(n); + getConvert()->fromStringArray(pvScalarArray,0,n,values,0); + bitSet->set(pvField->getFieldOffset()); + channelPut->put(pvStructure,bitSet); + return; + } + if(type==structure) { + PVScalarPtr pvScalar(pvStructure->getSubField("value.index")); + if(pvScalar) { + getConvert()->fromString(pvScalar,value); + bitSet->set(pvScalar->getFieldOffset()); + channelPut->put(pvStructure,bitSet); + return; + } + } + throw std::runtime_error(testChannel->getChannelName() + + " TestChannelPut::put not supported type"); +} + +class TestChannelMonitor; +typedef std::tr1::shared_ptr TestChannelMonitorPtr; + +class TestChannelMonitorRequester; +typedef std::tr1::shared_ptr TestChannelMonitorRequesterPtr; +typedef std::tr1::weak_ptr TestChannelMonitorRequesterWPtr; + +class TestChannelMonitorRequester +{ +public: + virtual void monitorEvent( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) = 0; +}; + +class TestChannelMonitor: + public MonitorRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannelMonitor); + virtual string getRequesterName(); + virtual void message(string const & message, MessageType messageType) {} + virtual void monitorConnect( + Status const & status, + MonitorPtr const & monitor, + StructureConstPtr const & structure); + virtual void monitorEvent(MonitorPtr const & monitor); + virtual void unlisten(MonitorPtr const & monitor); + static TestChannelMonitorPtr create( + TestChannelMonitorRequesterPtr const &putRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + void connect(); + void waitConnect(double timeout); + void stopEvents(); +private: + TestChannelMonitor( + TestChannelMonitorRequesterPtr const &putRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + + TestChannelMonitorRequesterWPtr monitorRequester; + TestChannelPtr testChannel; + PVStructurePtr pvRequest; + Event waitForConnect; + Monitor::shared_pointer channelMonitor; +}; + +TestChannelMonitorPtr TestChannelMonitor::create( + TestChannelMonitorRequesterPtr const &monitorRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) +{ + TestChannelMonitorPtr testChannelMonitor(new TestChannelMonitor(monitorRequester,testChannel,pvRequest)); + testChannelMonitor->connect(); + testChannelMonitor->waitConnect(5.0); + return testChannelMonitor; +} + +TestChannelMonitor::TestChannelMonitor( + TestChannelMonitorRequesterPtr const &monitorRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) + : monitorRequester(monitorRequester), + testChannel(testChannel), + pvRequest(pvRequest) +{ +} + +string TestChannelMonitor::getRequesterName() {return "TestChannelMonitor";} + +void TestChannelMonitor::monitorConnect( + Status const & status, + MonitorPtr const & monitor, + StructureConstPtr const & structure) +{ + waitForConnect.signal(); +} + + +void TestChannelMonitor::monitorEvent(MonitorPtr const & monitor) +{ + TestChannelMonitorRequesterPtr req(monitorRequester.lock()); + if(!req) return; + while(true) { + MonitorElementPtr monitorElement = monitor->poll(); + if(!monitorElement) return; + req->monitorEvent(monitorElement->pvStructurePtr,monitorElement->changedBitSet); + monitor->release(monitorElement); + } +} + + +void TestChannelMonitor::unlisten(MonitorPtr const & monitor) +{ +} + +void TestChannelMonitor::connect() +{ + channelMonitor = testChannel->getChannel()->createMonitor(shared_from_this(),pvRequest); + if(!channelMonitor) throw std::runtime_error(testChannel->getChannelName() + + " TestChannelMonitor::connect failed "); +} + +void TestChannelMonitor::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) { + channelMonitor->start(); + return; + } + throw std::runtime_error(testChannel->getChannelName() + + " TestChannelMonitor::waitConnect failed "); +} + +void TestChannelMonitor::stopEvents() +{ + channelMonitor->stop(); +} + +class TestClient; +typedef std::tr1::shared_ptr TestClientPtr; + +class TestClient: + public TestChannelGetRequester, + public TestChannelPutRequester, + public TestChannelMonitorRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestClient); + virtual void getDone( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + virtual void putDone(); + virtual void monitorEvent( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + static TestClientPtr create(string const &channelName,PVStructurePtr const & pvRequest); + void connect(); + void get(); + void put(string const & value); + void waitGet(double timeout); + void waitPut(double timeout); + void stopEvents(); +private: + TestClient(string const &channelName,PVStructurePtr const & pvRequest); + string channelName; + PVStructurePtr pvRequest; + TestChannelPtr testChannel; + TestChannelGetPtr testChannelGet; + TestChannelPutPtr testChannelPut; + TestChannelMonitorPtr testChannelMonitor; + Event waitForGet; + Event waitForPut; +}; + +TestClientPtr TestClient::create(string const &channelName,PVStructurePtr const & pvRequest) +{ + TestClientPtr testClient(new TestClient(channelName,pvRequest)); + testClient->connect(); + return testClient; +} + +TestClient::TestClient(string const &channelName,PVStructurePtr const & pvRequest) + : channelName(channelName), + pvRequest(pvRequest) +{ +} + +void TestClient::connect() +{ + testChannel = TestChannel::create(channelName); + testChannelGet = TestChannelGet::create(shared_from_this(),testChannel,pvRequest); + testChannelPut = TestChannelPut::create(shared_from_this(),testChannel); + testChannelMonitor = TestChannelMonitor::create(shared_from_this(),testChannel,pvRequest); +} + +void TestClient::getDone( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + testOk(pvStructure!=NULL,"pvStructure not null"); + testOk(pvStructure->getSubField("value")!=NULL,"value not null"); + testOk(pvStructure->getSubField("timeStamp")!=NULL,"timeStamp not null"); + testOk(pvStructure->getSubField("alarm")!=NULL,"alarm not null"); + if (DEBUG) std::cout << testChannel->getChannelName() + " TestClient::getDone" + << " bitSet " << *bitSet + << " pvStructure\n" << pvStructure << "\n"; + waitForGet.signal(); +} + +void TestClient::putDone() +{ + waitForPut.signal(); +} + +void TestClient::monitorEvent( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + if (DEBUG) std::cout << testChannel->getChannelName() + " TestClient::monitorEvent" + << " bitSet " << *bitSet + << " pvStructure\n" << pvStructure << "\n"; +} + +void TestClient::get() +{ + testDiag("TestClient::get %s", + testChannel->getChannelName().c_str()); + testChannelGet->get(); + if (DEBUG) cout << "TestClient::get() calling waitGet\n"; + waitGet(5.0); +} + +void TestClient::waitGet(double timeout) +{ + testOk(waitForGet.wait(timeout), + "waitGet(%s) succeeded", testChannel->getChannelName().c_str()); +} + +void TestClient::put(string const & value) +{ + testDiag("TestClient::put %s := %s", + testChannel->getChannelName().c_str(), value.c_str()); + testChannelPut->put(value); + waitPut(5.0); +} + +void TestClient::waitPut(double timeout) +{ + testOk(waitForPut.wait(timeout), + "waitPut(%s) succeeded", testChannel->getChannelName().c_str()); +} + +void TestClient::stopEvents() +{ + testChannelMonitor->stopEvents(); +} + +class TestIoc; +typedef std::tr1::shared_ptr TestIocPtr; + +class TestIoc : + public epicsThreadRunable +{ +public: + virtual void run(); + static TestIocPtr create(); + void start(); + void shutdown(); +private: +#ifndef USE_DBUNITTEST + std::auto_ptr thread; + const char *base; + const char *arch; +#endif +}; + +TestIocPtr TestIoc::create() +{ + return TestIocPtr(new TestIoc()); +} + +void TestIoc::start() +{ +#ifdef USE_DBUNITTEST + testdbPrepare(); + testdbReadDatabase("testIoc.dbd", NULL, NULL); + testIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("testCaProvider.db", NULL, NULL); + eltc(0); + testIocInitOk(); + eltc(1); +#else + base = getenv("EPICS_BASE"); + if (!base) + testAbort("Environment variable $EPICS_BASE not defined"); + arch = getenv("EPICS_HOST_ARCH"); + if (!arch) + testAbort("Environment variable $EPICS_HOST_ARCH not defined"); + setenv("EPICS_CA_ADDR_LIST", "localhost", 1); + setenv("EPICS_CA_AUTO_ADDR_LIST", "NO", 1); + + thread = std::auto_ptr(new epicsThread( + *this, + "testIoc", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +#endif +} + +void TestIoc::run() +{ +#ifndef USE_DBUNITTEST + // Base-3.14 doesn't provide the dbUnitTest APIs, and the CA + // tests with an embedded IOC fail with a Base before 3.16.2. + // This version only works on workstation targets, it runs the + // softIoc from Base as a separate process, using system(). + if(system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -x test -d ../testCaProvider.db")!=0) { + string message(base); + message += "/bin/"; + message += arch; + message += "/softIoc -d ../testCaProvider.db not started"; + throw std::runtime_error(message); + } +#endif +} + +void TestIoc::shutdown() +{ +#ifdef USE_DBUNITTEST + testIocShutdownOk(); + testdbCleanup(); +#endif +} + +void checkClient(const string &channelName, const string &putValue) +{ + string request("value,alarm,timeStamp"); + PVStructurePtr pvRequest(createRequest(request)); + TestClientPtr client = TestClient::create(channelName,pvRequest); + if (!client) + testAbort("NULL client for %s", channelName.c_str()); + client->put(putValue); + client->get(); + client->stopEvents(); +} + +MAIN(testCaProvider) +{ + testPlan(84 + EXIT_TESTS); + + TestIocPtr testIoc(new TestIoc()); + testIoc->start(); + + testDiag("===Test caProvider==="); + CAClientFactory::start(); + ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); + try { + ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); + if (!channelProvider) + testAbort("Channel provider 'ca' not registered"); + + checkClient("DBRlongout", "5"); + checkClient("DBRdoubleout", "1.5"); + checkClient("DBRstringout", "test"); + checkClient("DBRbyteArray", "1 2 3"); + checkClient("DBRshortArray", "1 2 3"); + checkClient("DBRintArray", "1 2 3"); + checkClient("DBRubyteArray", "1 2 3"); + checkClient("DBRushortArray", "1 2 3"); + checkClient("DBRuintArray", "1 2 3"); + checkClient("DBRfloatArray", "1 2 3"); + checkClient("DBRdoubleArray", "1 2 3"); + checkClient("DBRstringArray", "aa bb cc"); + checkClient("DBRmbbout", "2"); + checkClient("DBRbinaryout", "1"); + +#ifndef USE_DBUNITTEST + // put to record that makes IOC exit + string channelName = "test:exit"; + string request("value"); + PVStructurePtr pvRequest(createRequest(request)); + TestClientPtr client = TestClient::create(channelName,pvRequest); + if (!client) + testAbort("NULL client for %s", channelName.c_str()); + client->put("1"); + client->stopEvents(); +#endif + } + catch (std::exception& e) { + testAbort("caught un-expected exception: %s", e.what()); + } + + testIoc->shutdown(); + + return testDone();; +} + diff --git a/testCa/testCaProvider.db b/testCa/testCaProvider.db new file mode 100644 index 00000000..04cdcf79 --- /dev/null +++ b/testCa/testCaProvider.db @@ -0,0 +1,278 @@ + +record(calcout, "DBRcalcout") +{ + field(DESC, "calcout") + field(CALC, "(A