214 Commits
0.9.1 ... 4.2.0

Author SHA1 Message Date
Andrew Johnson
a96ec158bc Set cloudbees branches 2016-08-01 13:30:58 -05:00
Marty Kraimer
33852c6375 Merge pull request #20 from mrkraimer/master
change SHRLIB_VERSION
2016-07-22 15:10:47 -04:00
mrkraimer
ce9322eb9c change SHRLIB_VERSION 2016-07-22 14:45:02 -04:00
Ralph Lange
a634d53a00 jenkins: fix installE4 logic, add pvCommon dependency 2016-07-22 14:59:12 +02:00
Ralph Lange
515e7926b8 jenkins: use install functions, add BRANCH variable to doc script 2016-07-19 23:07:40 +02:00
Marty Kraimer
6877ec8397 Merge pull request #19 from mrkraimer/master
Documentation Changes
2016-07-19 06:01:59 -04:00
mrkraimer
03fc6f4a63 mainly doc changes 2016-07-18 13:01:35 -04:00
mrkraimer
1d80fbf512 fix LICENSE 2016-07-09 06:54:49 -04:00
mrkraimer
04688b1e1a fix LICENSE 2016-07-09 06:54:48 -04:00
mrkraimer
08751c619b Merge https://github.com/epics-base/pvDatabaseCPP 2016-07-07 09:48:05 -04:00
mrkraimer
0c96300e99 use LICENSE.txt from andrew 2016-07-07 09:41:51 -04:00
Marty Kraimer
e17d8bbd76 Merge pull request #18 from mrkraimer/master
Compatibility with pvDatabaseJava; on-line delete; database termination
2016-07-01 10:36:17 -04:00
mrkraimer
0784cc15d0 make more compatible with pvDatabaseJava; work on on-line delete and also termination 2016-07-01 10:27:04 -04:00
Marty Kraimer
81220c87f8 Merge pull request #17 from mrkraimer/master
on-line delete; notify clients when a PVRecord is deleted
2016-06-24 15:04:44 -04:00
mrkraimer
75849c6676 on-line delete; notify clients when a PVRecord is deleted 2016-06-24 14:51:55 -04:00
Marty Kraimer
5f98b5230a Merge pull request #16 from mrkraimer/master
don't issue maesage when being destroyed is true
2016-06-20 11:03:15 -04:00
mrkraimer
d3bbbdd083 don't issue maesage when being destroyed is true 2016-06-20 09:57:29 -04:00
Marty Kraimer
4197eb9422 Merge pull request #15 from mrkraimer/master
make void test() static
2016-06-15 12:20:10 -04:00
mrkraimer
349f7d0a58 make void test() static 2016-06-15 12:16:12 -04:00
Marty Kraimer
355cb68363 Merge pull request #14 from mrkraimer/master
use correct name in MAIN
2016-06-15 11:00:48 -04:00
mrkraimer
b5e1341736 use correct name in MAIN 2016-06-15 10:50:01 -04:00
Marty Kraimer
570fbf80ae Merge pull request #13 from mrkraimer/master
add removeRecord; other minor changes
2016-06-15 07:52:01 -04:00
mrkraimer
fde7953de0 add removeRecord; more work on termination issues 2016-06-15 07:21:43 -04:00
mrkraimer
e025e542ea work on RAII and memory leaks 2016-06-01 10:10:00 -04:00
Marty Kraimer
f7a3ca2c3c Merge pull request #12 from mrkraimer/master
register LocalChannelProvider; traceRecord consistant with pvDatabase…
2016-04-20 11:13:18 -04:00
mrkraimer
1eea87efc7 register LocalChannelProvider; traceRecord consistant with pvDatabaseJava 2016-04-19 10:14:39 -04:00
Andrew Johnson
15c6d32b61 Change cloudbees_doc script to use Base-3.15.3 2016-04-12 11:50:11 -05:00
Marty Kraimer
db2f528c0a Merge pull request #11 from anjohnson/master
Support for additional Windows targets
2016-04-12 11:50:04 -04:00
Andrew Johnson
351840b490 Use epicsShareFunc, not epicsShareExtern
Also needed on friend declarations.
2016-04-06 14:49:51 -05:00
Marty Kraimer
c36b969da4 Merge pull request #10 from mrkraimer/master
remove unused ContextLocal
2016-03-30 05:28:44 -04:00
mrkraimer
f4c0b54c17 remove unused ContextLocal 2016-03-29 09:39:02 -04:00
Marty Kraimer
0611db9a18 Merge pull request #9 from mrkraimer/master
for installing in include/pv follow  pvaClientCPP
2016-02-04 13:12:15 -05:00
mrkraimer
80ff17cfbc make test/rtemsTestHarness.c the same as epics-base 2016-02-04 08:30:51 -05:00
mrkraimer
1ea3d46baf set CHECK_RELEASE = WARN; remove TODO.html 2016-02-04 08:11:25 -05:00
Andrew Johnson
3babe530ca Fixes for RTEMS - routine name and comment 2016-02-03 11:40:16 -06:00
mrkraimer
6647ab3142 try to make win and rtems build 2016-02-03 07:17:56 -05:00
mrkraimer
0595b5a9d5 for installing in include/pv follow pvaClientCPP 2016-02-02 13:00:51 -05:00
Marty Kraimer
9c93cef34e Merge pull request #8 from mrkraimer/master
test/src/powerSupply.h make all methods inline
2016-01-28 13:31:28 -05:00
mrkraimer
3fc5aad6f5 Merge https://github.com/epics-base/pvDatabaseCPP 2016-01-27 09:49:37 -05:00
mrkraimer
4934e5a8c7 make all methods inline 2016-01-27 09:44:50 -05:00
Marty Kraimer
bb3aca734c Merge pull request #7 from mrkraimer/master
jenkins/cloudbees_build attempt to make vxWorks and RTEMS build
2016-01-27 07:13:59 -05:00
mrkraimer
ce41acd373 add SHRLIB_VERSION 2016-01-27 07:07:52 -05:00
mrkraimer
264100df7c added make runtests 2016-01-26 10:04:36 -05:00
mrkraimer
64909f2152 jenkins/cloudbees_build attempt to make vxWorks and RTEMS build
src/pv/pvDatabase.h attemp t make doc build
src/pvAccess/channelLocal.cpp trap exceptions and turn into Status
2016-01-26 08:33:00 -05:00
Marty Kraimer
f0320e7173 Merge pull request #6 from anjohnson/master
Use epicsGuard and epicsGuardRelease for lock handling
2016-01-25 07:17:19 -05:00
Marty Kraimer
7f15b136b1 Merge pull request #5 from mrkraimer/master
remove examples, make test regression test, update documentation
2016-01-25 07:10:07 -05:00
Andrew Johnson
6c08a05b81 Use epicsGuard and epicsGuardRelease
These objects provide exception-safe locking and unlocking.
2016-01-22 12:05:23 -06:00
mrkraimer
326dc6ca69 fix some errors in documentation/pvDatabaseCPP.html 2016-01-22 10:42:48 -05:00
mrkraimer
751cc8965d forgot to add some files before last commit 2016-01-21 14:57:00 -05:00
mrkraimer
102174913c remove examples (moved to exampleCPP) ; test is now a regresion test (make runtests) 2016-01-21 14:39:23 -05:00
Marty Kraimer
61c41ceb7e Merge pull request #4 from mrkraimer/master
use weak pointer for callbacks
2016-01-21 14:29:07 -05:00
mrkraimer
4ac867ec0f use weak pointer for callbacks 2016-01-07 07:44:08 -05:00
Marty Kraimer
ca3573291a Merge pull request #3 from dhickin/rpc
Add support for Channel RPC
2015-12-09 06:19:44 -05:00
mrkraimer
3764c3ad36 call unlisten before erase 2015-12-08 15:45:54 -05:00
Dave Hickin
0aadb3b0a7 Make ChannelRPCLocal follow pvDatabase conventions
Make ChannelRPCLocal print debug according to TraceLevel. Move function
implementations out of class body. Follow pvDatabase general coding
conventions (variable names, order of declarations, indentation etc.).
2015-12-08 13:40:04 +00:00
Dave Hickin
bc3335d4f9 Add example of record which supports RPC and a client
Record has x and y-coordinate fields and a timestamp and also provides
a service which sets (x,y) to a sequence of values.

An RPC client application (move) sends positions as an array.
2015-12-08 13:38:47 +00:00
Dave Hickin
a99b08fd02 Add support for RPC to pvDatabase
Add a new function to PVRecord which returns a service (from the
pvAccess RPC library) for a supplied pvRequest (defaults to returning
a null pointer).

Add an implementation of ChannelRPC which gets the service from the
record and is used by ChannelLocal.
2015-12-08 10:37:37 +00:00
mrkraimer
c717138c7d get rid of two more references to pvIOC 2015-10-28 07:29:48 -04:00
mrkraimer
e6e7902831 removed all talk about pvIOC 2015-10-27 13:01:03 -04:00
Ralph Lange
d71d3f1862 jenkins: build dependency branches must be empty on master 2015-10-26 14:02:57 +01:00
mrkraimer
77ff6ad6c1 Merge branch 'release/4.1' 2015-10-21 07:17:48 -04:00
mrkraimer
b3b01959e5 mispelled releaase 2015-10-07 10:35:59 -04:00
mrkraimer
9db7ed360b mv documentation/RELEASE_NOTES.md to RELEASE_NOTES.md 2015-10-07 10:29:39 -04:00
mrkraimer
1f7881010e fix doxygen errors 2015-10-03 06:19:15 -04:00
mrkraimer
90a96f4ee4 update documentation; remove recordList 2015-10-02 08:48:30 -04:00
Ralph Lange
34ff6077c7 jenkins: fix CloudBees doc job 2015-09-28 15:28:17 +02:00
Ralph Lange
1050b980ec jenkins: fix CloudBees doc job 2015-09-28 15:27:47 +02:00
Ralph Lange
2bdb012709 jenkins: change doc script to adapt to new CloudBees jobs 2015-09-14 15:02:10 +02:00
Ralph Lange
8a1690f35a jenkins: change doc script to adapt to new CloudBees jobs 2015-09-14 15:00:43 +02:00
Ralph Lange
b20fd4c5c5 jenkins: set dependency versions (part of epics-base/jenkins-jobs#10) 2015-09-04 13:02:29 +02:00
Ralph Lange
d578dfbb99 REVERT jenkins: set dependency versions
(reverted from commit e9d14d061e)
2015-09-04 13:01:12 +02:00
Ralph Lange
e9d14d061e jenkins: set dependency versions (part of epics-base/jenkins-jobs#10) 2015-09-04 12:59:57 +02:00
Ralph Lange
409b045779 jenkins: add configurable dependent branch versions 2015-09-03 13:50:03 +02:00
mrkraimer
fda81767a5 update README 2015-09-03 06:40:42 -04:00
Ralph Lange
fe1b167e23 jenkins: remove hgweb job; split build and doc jobs; update EPICS Base version 2015-08-18 16:27:57 +02:00
Ralph Lange
dc0f20cc5f Add QtCreator pattern to .gitignore 2015-08-18 16:14:36 +02:00
dhickin
6e7d887e6e Merge pull request #2 from dhickin/replace_calls_getScalarArrayField
Replace calls of deprecated PVStructure::getScalarArrayField
2015-07-28 17:01:30 +01:00
Dave Hickin
699a6cd8b4 Remove unused argument in testPVScalarArray 2015-07-24 10:45:01 +01:00
Dave Hickin
70454a6006 Remove calls of deprecated getScalarArrayField 2015-07-24 10:42:45 +01:00
dhickin
deebe46378 Merge pull request #1 from dhickin/replace_deprecated_getDerivedTypeField
Replace calls of deprecated functions for getting subfields of a PVStructure
2015-07-23 16:36:20 +01:00
Ralph Lange
18dc4279ad jenkins: fix URL for pvAccessCPP tar 2015-07-17 17:23:14 +02:00
Ralph Lange
61e9dc0368 jenkins: use pvAccessCPP without microbench 2015-07-17 17:09:14 +02:00
Dave Hickin
6b0d1ce49b Replace calls of deprecated subfield functions
The non-template functions for getting subfields of a PVStructure, such
as PVStructure::getIntField were marked as deprecated in the 4.4 release
and now generate build warnings. Replace calls of these functions with
calls of the template function getSubField, e.g. getSubField<PVInt>.
2015-07-15 19:06:04 +01:00
Marty Kraimer
81b160d7b6 finish hg to git; remove qtcreator files 2015-06-09 09:28:12 -04:00
dhickin
4e22a056d2 Fixed win32 build error. 2015-06-08 13:12:15 +01:00
Marty Kraimer
4af5ba92d9 had wrong name for ifdef 2015-03-30 06:39:39 -04:00
Matej Sekoranja
e79c70c74c ContextLocal added 2015-02-24 10:59:30 +01:00
Marty Kraimer
c0694e2d69 remove plugin from exampleDatabase 2015-02-23 16:14:17 -05:00
Matej Sekoranja
36dac883f4 using new copy API 2015-02-18 10:04:19 +01:00
Andrew Johnson
35dcbf35b3 Fix */iocBoot/Makefile include lines 2015-02-09 11:44:02 -06:00
Marty Kraimer
30dd2ed046 simplify monitors; cleanup code; fix race condidition for monitor cleanup 2015-02-06 14:49:28 -05:00
Ralph Lange
e30e4f3638 jenkins: fix cloudbees script (commands must return 0) 2014-12-19 14:22:01 +01:00
Ralph Lange
15578d1647 jenkins: create and update documentation in only one build configuration 2014-12-19 13:18:45 +01:00
Ralph Lange
e3e270e242 jenkins: fix artefact (binary CB distribution tar) name 2014-12-13 16:00:01 +01:00
Marty Kraimer
ec44251df0 changes for doxygen 2014-12-12 09:28:49 -05:00
Marty Kraimer
3344165f19 commit after merge 2014-12-12 08:20:07 -05:00
Marty Kraimer
c2ea402be0 one more race condition when server server exits 2014-12-12 08:18:56 -05:00
Ralph Lange
0535ff990d Merge jenkins changes 2014-12-12 13:24:11 +01:00
Ralph Lange
18ff90e641 jenkins: make EPICS base a parameter in jenkins job 2014-12-12 13:21:37 +01:00
Marty Kraimer
a781195458 more changes for possible race conditions in pvCopyMonitor.cpp 2014-12-12 07:03:55 -05:00
Marty Kraimer
1a641f0d1b fix race condition between startMonitoring and beginGroupPut in pvCopyMonitor.cpp 2014-12-12 06:25:45 -05:00
Marty Kraimer
0e6639c149 instead of just isDestroyed monitorFactory keeps state idle,active,destroyed 2014-12-09 09:31:32 -05:00
Marty Kraimer
93ba23aeb8 merge branch release/4.0 2014-12-04 09:22:25 -05:00
dhickin
7743f3e3fd make "local" *.local files take precedence over those in TOP/.. 2014-12-03 16:52:01 +00:00
dhickin
e9ce6a2f0b include CONFIG_SITE.local instead of CONFIG.local at top-level. 2014-12-03 16:10:25 +00:00
Marty Kraimer
1ed48c15f1 Added tag 4.0.1 for changeset 541b0a7a645c 2014-11-11 07:07:34 -05:00
Marty Kraimer
d27f929595 merge from default branch 2014-11-11 06:52:56 -05:00
Matej Sekoranja
b4e17f271b win32 vs2013 compilation fix 2014-11-11 09:05:25 +01:00
Marty Kraimer
79f407486a in the future remove recordList since pvAccess provides pvlist 2014-11-10 10:24:07 -05:00
Matej Sekoranja
3c359728f7 Added tag 4.0.0 for changeset 85d46a2614f9 2014-10-29 13:17:22 +01:00
Matej Sekoranja
7b9693562a merge 2014-10-29 13:15:33 +01:00
Matej Sekoranja
f1c39ca5d2 clang compilation fixes 2014-10-16 07:30:42 +02:00
Marty Kraimer
16b1775b98 Added tag 4.0.0 for changeset 42dbe8a17f85 2014-10-15 08:37:24 -04:00
Marty Kraimer
f6ee7333bb merge changes from default 2014-10-15 07:29:49 -04:00
Matej Sekoranja
9f45bdfa75 missing epicsShareClass PowerSupply 2014-10-14 21:26:07 +02:00
Matej Sekoranja
03aa15b5f7 added missing lib for win32 2014-10-14 20:41:36 +02:00
Matej Sekoranja
8093952ca2 win32 test dll linkage 2014-10-14 07:40:34 +02:00
Marty Kraimer
2bea54e218 updated pvDatabaseCPP.html and TODO 2014-10-09 09:33:13 -04:00
dhickin
e81230dba5 Corrected spelling of synchrotron in licence. 2014-10-04 02:45:53 +01:00
dhickin
50b8f306c3 Spelling and typos. 2014-10-04 01:39:14 +01:00
Marty Kraimer
65be8e5678 Added tag 4.0.0 for changeset e2e041fa7d04 2014-10-01 09:01:22 -04:00
Marty Kraimer
dbb9310adc flow: Created branch 'release/4.0'. 2014-10-01 08:58:34 -04:00
Matej Sekoranja
50fbb396e8 win32 compilation of test and examples 2014-09-13 23:40:41 +02:00
Matej Sekoranja
ff19fe1cd8 win32 linkage 2014-09-13 23:01:29 +02:00
Matej Sekoranja
230938220e win compilation 2014-09-13 00:18:25 +02:00
Matej Sekoranja
334ed3b70a hopefully fixed windows exports 2014-09-12 10:00:00 +02:00
Marty Kraimer
4c7e51d8ad prepare documentation for pre release 2014-09-03 09:13:45 -04:00
Marty Kraimer
4973a6297e fixed bug in creating implementation of channelArray 2014-08-29 09:33:42 -04:00
Marty Kraimer
f0d1481a28 nanoSecond => nanosecond 2014-08-20 06:36:40 -04:00
Marty Kraimer
d5235db54c mainly documentation changes; did much testing 2014-08-11 14:18:49 -04:00
Marty Kraimer
b125035a11 fixed connection problems 2014-08-07 14:36:35 -04:00
Marty Kraimer
9551b0e4c6 fixed bugs in implementation of montor; connection problems still exist 2014-08-07 08:02:53 -04:00
Marty Kraimer
d6aa03815e changes because of ChannelArray API change; update TODO.md; add RELEASE_NOTES.html and TODO.html 2014-07-22 08:53:32 -04:00
Marty Kraimer
ce0d62fbbc changed upcoming release number. 2014-07-10 13:38:06 -04:00
Marty Kraimer
2fe3e66047 updated documentation; fixed bugs while updating documentation 2014-07-10 13:25:58 -04:00
dhickin
fa53d72258 Corrected includes for CONFIG_SITE files. 2014-07-02 12:27:00 +01:00
Marty Kraimer
b010cf0849 add some records for testing easyPVAJava 2014-07-02 07:11:50 -04:00
Matej Sekoranja
7fd707cb4b Added tag 3.1.0 for changeset 395f48d5196d 2014-07-02 00:12:37 +02:00
Marty Kraimer
5d823307f0 make records double00, ..., double04 2014-06-25 09:16:31 -04:00
Marty Kraimer
d41d5726d2 add records for testing easyPVAJava multiChannel 2014-06-24 15:05:19 -04:00
Matej Sekoranja
723f98bc44 added extern C on epicsExportRegistrar 2014-06-20 09:16:35 +02:00
Matej Sekoranja
1544147bdd win dll linkage 2014-06-20 08:56:39 +02:00
Matej Sekoranja
0447441cfa toString replaced with <<operator 2014-06-20 08:38:54 +02:00
Matej Sekoranja
8ce42ebb9a String -> std::string, toString methods removed, take 2 2014-06-19 14:55:44 +02:00
Matej Sekoranja
c39b966121 String -> std::string, toString methods removed 2014-06-19 14:30:40 +02:00
Marty Kraimer
61edf17cdf fix bug related to stride 2014-06-13 09:53:46 -04:00
Matej Sekoranja
39f537d7da win port: visibility declarations added/fixed 2014-06-13 11:43:22 +02:00
Matej Sekoranja
ba496de2d3 VxWorks fix: there is no Status::Status type 2014-06-13 11:04:29 +02:00
Marty Kraimer
38574ed76f support for ChannelArray UnionArray 2014-06-12 15:27:26 -04:00
Marty Kraimer
92be294bbf merge feature/changesAfter3_0_2; resolve conflicts 2014-06-10 15:53:37 -04:00
Marty Kraimer
35429bf4df flow: Merged <feature> 'changesAfter3_0_2' to <develop> ('default'). 2014-06-10 09:05:48 -04:00
Marty Kraimer
552925dfe6 flow: Closed <feature> 'changesAfter3_0_2'. 2014-06-10 08:55:18 -04:00
Marty Kraimer
d1791393ad Added tag before_merge_changesAfter3_0_2 for changeset abdc90bf52a0 2014-06-10 08:53:33 -04:00
Matej Sekoranja
3dbad700f7 channelList implemented 2014-06-09 22:19:52 +02:00
Marty Kraimer
c06e33e197 better implementation of ChannelArray::getLength 2014-06-09 07:55:08 -04:00
Marty Kraimer
1d8f01517a more work on stride 2014-06-06 11:28:41 -04:00
Marty Kraimer
8c1b142e48 working on support for stride for ChannelArray 2014-06-05 10:23:51 -04:00
Marty Kraimer
ca27cb5e3c changes for new pvAccess API 2014-06-04 10:48:12 -04:00
Andrew Johnson
8fb02d5602 flow: Merged <feature> 'housekeeping' to <develop> ('default'). 2014-04-29 10:53:06 -05:00
Andrew Johnson
38f8f1de51 flow: Closed <feature> 'housekeeping'. 2014-04-29 10:53:06 -05:00
Marty Kraimer
a5fa17aca7 pvAccess added method cancel 2014-04-23 11:01:14 -04:00
Marty Kraimer
fe62a7181f use copy and monitor from pvDataCPP; imlement example plugin; algorithm => plugin 2014-04-23 09:25:58 -04:00
Andrew Johnson
69fe610c5d Merged changes from default branch 2014-04-18 17:54:35 -05:00
Andrew Johnson
83f2fa9d9a epicsExportRegistrar() must be inside extern "C" block 2014-04-18 17:53:12 -05:00
Andrew Johnson
5faa9902ea DBD file cleanup
Renamed DBD files that only contain registrar entries.
Removed the ...Include.dbd files, create them from the Makefile.
2014-04-18 17:40:17 -05:00
Andrew Johnson
f10a5f8279 Rename arrayPerformance to arrayPerfTop 2014-04-17 15:46:26 -05:00
Andrew Johnson
fcb82fd31f Rename test to testTop and adjust build config 2014-04-17 15:35:29 -05:00
Andrew Johnson
16fbd0f205 Merged Windows port changes from default branch. 2014-04-16 16:29:12 -05:00
Matej Sekoranja
597bca1ca5 Windows port (visibility declrations). 2014-04-14 18:11:12 +02:00
Andrew Johnson
3e790e34e3 Move example and test headers out of include/pv
The only headers installed there should be
those for the official API of the module.
2014-04-10 16:01:32 -05:00
Andrew Johnson
144da546ea Clean up top-level Makefiles, fix dependencies 2014-04-10 15:57:38 -05:00
Andrew Johnson
2d450bdd5b Unify configure files with the other V4 modules 2014-04-10 15:53:36 -05:00
Andrew Johnson
333bbca2f2 Fix exampleDatabase/Makefile dependencies 2014-04-04 14:50:30 -05:00
Andrew Johnson
d17192804e Correct and expand ExampleRELEASE.local 2014-04-03 17:31:47 -05:00
Andrew Johnson
63e7401ff1 Fix instructions in RELEASE file 2014-04-02 16:55:53 -05:00
Andrew Johnson
42e2e72474 Enable CHECK_RELEASE warnings 2014-04-02 16:55:38 -05:00
Marty Kraimer
342ab45dc8 changes for changesAfter3_0_2
pvCopy modified and moved to pvDataCPP
2014-04-01 14:07:43 -04:00
Andrew Johnson
c91f2e7263 Split src/Makefile into fragments 2014-03-28 15:29:31 -05:00
Andrew Johnson
f3dc67e620 flow: Created branch 'feature/housekeeping'. 2014-03-28 15:22:24 -05:00
Marty Kraimer
127830e3c7 flow: Created branch 'feature/changesAfter3_0_2'. 2014-03-25 07:18:10 -04:00
Marty Kraimer
9d3873c3fe flow initialization: Added configuration file. 2014-03-25 07:18:00 -04:00
Marty Kraimer
3ce6df5b0d add include; files for qtCreater 2014-03-24 09:58:42 -04:00
Marty Kraimer
0f1c0c28d0 bug found in Java implementation. Make corresponding change in C++ mimplementation. 2014-03-04 07:00:17 -05:00
Marty Kraimer
e740687635 pvaSrv is automatically started 2014-03-03 10:13:32 -05:00
Marty Kraimer
d1df670c83 pvCopy : do not "collapse" structures 2014-03-03 09:06:19 -05:00
Marty Kraimer
6b47485810 correct order of dependent LIBS; update doc for latest build conventions 2014-02-19 14:35:39 -05:00
Marty Kraimer
228b46a622 remove Support from generated library name to be consistent with other examples. 2014-02-19 08:39:59 -05:00
Ralph Lange
e932c01cf1 Fix: moved embedded top rules to TOP/configure/RULES_TOP 2014-02-14 14:45:53 +01:00
Ralph Lange
c6f6465457 More RELEASE cleanup 2014-02-14 14:45:11 +01:00
Ralph Lange
068d3070e2 Makefile: comment-out inter-app dependency definitions (not working) 2014-02-14 14:43:59 +01:00
Ralph Lange
4ad2e56a08 Fix: add all directories to TOP Makefile 2014-02-14 15:00:06 +01:00
Ralph Lange
d63610c7f5 Add Jenkins CI jobs for CloudBees build and hgweb service 2014-02-14 13:23:21 +01:00
Ralph Lange
92509abde8 Add dependency of exampleDatabase and examplePowerSupply to test 2014-02-14 13:17:58 +01:00
Ralph Lange
c445368537 Add RULES_TOP and top Makefile definitions to build embedded TOPs (test, examples) as part of the main build 2014-02-14 12:28:02 +01:00
Ralph Lange
2269a5bd66 Add RELEASE file improvements to arrayPerformance 2014-02-14 11:31:40 +01:00
Ralph Lange
876ec8062f Improve RELEASE files to be slim, self-contained, portable - including test and examples 2014-02-14 10:42:36 +01:00
Marty Kraimer
53c3901099 pvDatabaseCPP/src/V3IOC/* has been moved to pvAccessCPP; epicsShare has been added. 2014-02-13 07:06:14 -05:00
Matej Sekoranja
51f4820c24 src/V3IOC removed, moved to pvAccessCPP 2014-02-13 00:00:23 +01:00
Marty Kraimer
a21189cfda these should only be created by user 2014-02-07 14:40:36 -05:00
Marty Kraimer
61d884334a more work in examples; documentation is now up to date 2014-02-07 13:57:32 -05:00
Marty Kraimer
9a798bc05a more work on examples; documentation is only changed up to exampleServer 2014-02-06 16:46:47 -05:00
Marty Kraimer
94bd84211b move all examples to separate top level build areas.
Documentation needs updating.
2014-02-05 11:57:35 -05:00
Marty Kraimer
5d0718ab3a prepare for moving everything in src/V3IOC to pvAccessCPP 2014-02-04 09:21:50 -05:00
Marty Kraimer
e8a9771d1e try the make channel->destroy() followed immediately by recreate work but failed 2013-11-22 09:21:11 -05:00
Marty Kraimer
ac971042de add epicsThreadSleep between channel destroy and recreate 2013-11-21 06:24:28 -05:00
Marty Kraimer
ce116eefb8 added longArrayGet and longArrayPut; much more testing 2013-11-20 10:41:29 -05:00
Marty Kraimer
3e1a405d01 changed monitor queue implementation 2013-11-13 09:56:18 -05:00
Marty Kraimer
3039e1cdb0 more work on monitor queues 2013-11-12 11:09:25 -05:00
Marty Kraimer
006859120e make compatible with latest pvDataCPP and pvAccessCPP; lots of work on queues. 2013-11-06 15:44:58 -05:00
Marty Kraimer
14b3640e9a latest changes; memory leaks fixed 2013-10-16 08:13:15 -04:00
dhickin
ed5bf2d79b Fixed build error.Removed 'typename' from non template function. 2013-10-04 23:36:29 +01:00
Marty Kraimer
313ba68a06 more changes for arrayPerformance; added vectorPerformanceMain.cpp 2013-09-04 14:10:02 -04:00
Marty Kraimer
22786bb07e html syntax change 2013-08-28 10:55:20 -04:00
Marty Kraimer
534671dbe8 Added tag 0.9.1 for changeset bba6a2491bdf 2013-08-28 10:51:53 -04:00
129 changed files with 6139 additions and 17790 deletions

13
.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
bin/
lib/
doc/
include/
db/
dbd/
documentation/html
documentation/*.tag
envPaths
configure/*.local
!configure/ExampleRELEASE.local
**/O.*
QtC-*

View File

@@ -1,11 +0,0 @@
^QtC-
^bin/
^lib/
^doc/
^include/
^db/
^dbd/
^documentation/html
envPaths
configure/.*\.local
/O\..*

2588
Doxyfile

File diff suppressed because it is too large Load Diff

77
LICENSE
View File

@@ -1,12 +1,17 @@
Copyright and License Terms
---------------------------
Copyright (c) 2008 Martin R. Kraimer
Copyright (c) 2006 The University of Chicago, as Operator of Argonne
Copyright (c) 2006-2016 Martin R. Kraimer
Copyright (c) 2006-2016 UChicago Argonne LLC, as Operator of Argonne
National Laboratory.
Copyright (c) 2006 Deutsches Elektronen-Synchroton,
Copyright (c) 2006 Deutsches Elektronen-Synchrotron,
Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
Copyright (c) 2007 Control System Laboratory,
Copyright (c) 2007-2016 Control System Laboratory,
(COSYLAB) Ljubljana Slovenia
Copyright (c) 2010-2016 Brookhaven Science Associates, as Operator
of Brookhaven National Laboratory
Copyright (c) 2011-2016 Diamond Light Source Limited,
(DLS) Didcot, United Kingdom
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
@@ -31,48 +36,30 @@ OTHER DEALINGS IN THE SOFTWARE.
________________________________________________________________________
This software is in part copyrighted by the University of Chicago (UofC)
Additional Disclaimers
----------------------
In no event shall UofC be liable to any party for direct, indirect,
special, incidental, or consequential damages arising out of the use of
this software, its documentation, or any derivatives thereof, even if
UofC has been advised of the possibility of such damage.
This software is copyright in part by these institutions:
UofC specifically disclaims any warranties, including, but not limited
to, the implied warranties of merchantability, fitness for a particular
purpose, and non-infringement. This software is provided on an "as is"
basis, and UofC has no obligation to provide maintenance, support,
updates, enhancements, or modifications.
* Brookhaven Science Associates, as Operator of Brookhaven
National Laboratory, New York, USA
* Control System Laboratory, Ljubljana, Slovenia
* Deutsches Elektronen-Synchroton, Member of the Helmholtz
Association, Hamburg, Germany
* Diamond Light Source Limited, Didcot, United Kingdom
* Helmholtz-Zentrum Berlin fuer Materialien und Energie m.b.H.,
Berlin, Germany.
* UChicage Argonne LLC, as Operator of Argonne National Laboratory,
Illinois, USA
________________________________________________________________________
In no event shall these institutions be liable to any party for direct,
indirect, special, incidental, or consequential damages arising out of
the use of this software, its documentation, or any derivatives thereof,
even if advised of the possibility of such damage.
This software is in part copyrighted by the BERLINER SPEICHERRING
GESELLSCHAFT FUER SYNCHROTRONSTRAHLUNG M.B.H. (BESSY), BERLIN, GERMANY.
These institutions specifically disclaim any warranties, including, but
not limited to, the implied warranties of merchantability, fitness for a
particular purpose, and non-infringement. This software is provided on
an "as is" basis, and these institutions have no obligation to provide
maintenance, support, updates, enhancements, or modifications.
In no event shall BESSY be liable to any party for direct, indirect,
special, incidental, or consequential damages arising out of the use of
this software, its documentation, or any derivatives thereof, even if
BESSY has been advised of the possibility of such damage.
BESSY specifically disclaims any warranties, including, but not limited
to, the implied warranties of merchantability, fitness for a particular
purpose, and non-infringement. This software is provided on an "as is"
basis, and BESSY has no obligation to provide maintenance, support,
updates, enhancements, or modifications.
________________________________________________________________________
This software is in part copyrighted by the Deutsches Elektronen-Synchroton,
Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
In no event shall DESY be liable to any party for direct, indirect,
special, incidental, or consequential damages arising out of the use of
this software, its documentation, or any derivatives thereof, even if
DESY has been advised of the possibility of such damage.
DESY specifically disclaims any warranties, including, but not limited
to, the implied warranties of merchantability, fitness for a particular
purpose, and non-infringement. This software is provided on an "as is"
basis, and DESY has no obligation to provide maintenance, support,
updates, enhancements, or modifications.
________________________________________________________________________

View File

@@ -6,11 +6,7 @@ DIRS += configure
DIRS += src
src_DEPEND_DIRS = configure
DIRS += example
DIRS += test
test_DEPEND_DIRS = src
DIRS += iocBoot
include $(TOP)/configure/RULES_TOP

38
README.md Normal file
View File

@@ -0,0 +1,38 @@
pvaDatabaseCPP
============
A brief description of a pvDatabase is that it is a set of network accessible, smart, memory resident records. Each record has data composed of a top level PVStructure. Each record has a name which is the channelName for pvAccess. A local Channel Provider implements the complete ChannelProvider and Channel interfaces as defined by pvAccess. The local provider provides access to the records in the pvDatabase. This local provider is accessed by the remote pvAccess server. A record is smart because code can be attached to a record, which is accessed via a method named process.
pvaDatabase is a synchronous Database interface to pvAccess,
which is callback based.
pvaDatabase is thus easier to use than pvAccess itself.
See documentation/pvaDatabaseCPP.html for details.
Building
--------
If a proper RELEASE.local file exists one directory level above pvaDatabaseCPP
then just type:
make
It can also be built by:
cp configure/ExampleRELEASE.local configure/RELEASE.local
edit configure/RELEASE.local
make
Examples
------------
Examples are available in exampleCPP.
Status
------
* The API is for EPICS Version 4 release 4.6.0

View File

@@ -14,26 +14,11 @@
# Set CHECK_RELEASE to NO to disable checking completely.
# Set CHECK_RELEASE to WARN to perform consistency checking but
# continue building anyway if conflicts are found.
#CHECK_RELEASE = YES
# Set this when you only want to compile this application
# for a subset of the cross-compiled target architectures
# that Base is built for.
#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040
CHECK_RELEASE = WARN
# To install files into a location other than $(TOP) define
# INSTALL_LOCATION here.
#INSTALL_LOCATION=</path/name/to/install/top>
# Set this when your IOC and the host use different paths
# to access the application. This will be needed to boot
# from a Microsoft FTP server or with some NFS mounts.
# You must rebuild in the iocBoot directory for this to
# take effect.
#IOCS_APPL_TOP = </IOC/path/to/application/top>
INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv
USR_INCLUDES += -I $(INSTALL_LOCATION)/include
-include $(TOP)/../CONFIG_SITE.local
-include $(TOP)/configure/CONFIG_SITE.local
-include $(TOP)/../CONFIG.local

View File

@@ -0,0 +1,8 @@
EPICS4_DIR=/home/epicsv4/master
PVACCESS=${EPICS4_DIR}/pvAccessCPP
PVDATA=${EPICS4_DIR}/pvDataCPP
PVCOMMON=${EPICS4_DIR}/pvCommonCPP
TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top
EPICS_BASE=/home/install/epics/base

View File

@@ -6,3 +6,4 @@ TARGETS = $(CONFIG_TARGETS)
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
include $(TOP)/configure/RULES

View File

@@ -1,11 +1,11 @@
# RELEASE - Location of external support modules
# pvDatabaseCPP RELEASE - Location of external support modules
#
# IF YOU MAKE ANY CHANGES to this file you must subsequently
# do a "gnumake rebuild" in this application's top level
# directory.
# IF YOU CHANGE this file or any file it includes you must
# subsequently do a "gnumake rebuild" in the application's
# top level directory.
#
# The build process does not check dependencies against files
# that are outside this application, thus you should do a
# that are outside this application, thus you should also do a
# "gnumake rebuild" in the top level directory after EPICS_BASE
# or any other external module pointed to below is rebuilt.
#
@@ -13,32 +13,15 @@
# RELEASE.$(EPICS_HOST_ARCH).Common
# RELEASE.Common.$(T_A)
# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
# EPICS V4 Developers: Do not edit the locations in this file!
#
# This file should ONLY define paths to other support modules,
# or include statements that pull in similar RELEASE files.
# Build settings that are NOT module paths should appear in a
# CONFIG_SITE file.
# Create a file RELEASE.local pointing to your PVASRV, PVACCESS,
# PVDATA, PVCOMMON and EPICS_BASE build directories, e.g.
# PVACCESS = /path/to/epics/pvAccessCPP
# PVDATA = /path/to/epics/pvDataCPP
# PVCOMMON = /path/to/epics/pvCommonCPP
# EPICS_BASE = /path/to/epics/base
TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top
# If using the sequencer, point SNCSEQ at its top directory:
#SNCSEQ=$(EPICS_BASE)/../modules/soft/seq
# EPICS_BASE usually appears last so other apps can override stuff:
# do not edit the locations in this file
# create RELEASE.local with the paths to your EPICS_BASE, PVDATA, and PVACCESS
# these default locations are needed for the BNL Jenkins server to work
# Set RULES here if you want to take build rules from somewhere
# other than EPICS_BASE:
#RULES=/path/to/epics/support/module/rules/x-y
# Leave these in for the Jenkins build at BNL to work
EPICS_BASE=/home/install/epics/base
PVDATA=/home/mrk/hg/pvDataCPP
PVACCESS=/home/mrk/hg/pvAccessCPP
# set your EPICS_BASE, PVDATA and PVACCESS paths in here
-include $(TOP)/configure/RELEASE.local
-include $(TOP)/../RELEASE.local
-include $(TOP)/configure/RELEASE.local

View File

@@ -1,3 +1,16 @@
#RULES_TOP
include $(CONFIG)/RULES_TOP
EMTOPACTIONS += cvsclean realuninstall
distclean: emtop_distclean
emtopDistcleanTargets += $(foreach dir, $(EMBEDDED_TOPS), \
$(dir)$(DIVIDER)emtop_dummy_action)
$(emtopDistcleanTargets) :
$(MAKE) -C $(dirPart) $(EMTOPACTIONS)
emtop_distclean : $(emtopDistcleanTargets)
.PHONY : $(emtopDistcleanTargets)

View File

@@ -0,0 +1,23 @@
EPICS V4 release 4.6
====================
* The examples are moved to exampleCPP
* Support for channelRPC is now available.
* removeRecord and traceRecord are now available.
The test is now a regression test the can be ran via
make runtests
EPICS V4 release 4.5
====================
This release is one component of EPICS V4 release 4.5.
This is the first release of pvDatabaseCPP.
It provides functionality equivalent to pvDatabaseJava.

19
documentation/TODO.md Normal file
View File

@@ -0,0 +1,19 @@
TODO
===========
monitorPlugin
-------------
A debate is on-going about what semantics should be.
Must test record delete.
-------------------
Must test removing a record from the PVDatabase while a pvAccess client
is attached. Also why do both unlisten and detach exists?
create more regression tests
----------------
Currently only some simple tests exist. Most of the testing has been via the examples

File diff suppressed because it is too large Load Diff

View File

@@ -1,733 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>pvDatabaseCPP</title>
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/base.css" />
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
<style type="text/css">
/*<![CDATA[*/
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
table { margin-left: auto; margin-right: auto }
.diagram { text-align: center; margin: 2.5em 0 }
span.opt { color: grey }
span.nterm { font-style:italic }
span.term { font-family:courier }
span.user { font-family:courier }
span.user:before { content:"<" }
span.user:after { content:">" }
.nonnorm { font-style:italic }
p.ed { color: #AA0000 }
span.ed { color: #AA0000 }
p.ed.priv { display: inline; }
span.ed.priv { display: inline; }
/*]]>*/</style>
<!-- Script that generates the Table of Contents -->
<script type="text/javascript"
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
</script>
</head>
<body>
<div class="head">
<h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 27-Nov-2012</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>This version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>Previous version:</dt>
<dd>None</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
</dl>
<p class="copyright">This product is made available subject to acceptance of the <a
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
<hr />
</div>
<h2 class="nocount">Abstract</h2>
<p>This document describes pvDatabaseCPP,
which is a framework for implementing a network accessable database of smart memory resident
records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by
pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess.
The framework must be extended in order to create record instances.
The minimum that an extenson must provide is a top level PVStructure and a process method
but the framework provides for complex extensions.</p>
<p>EPICS version 4 is a set of related products in the EPICS
V4 control system programming environment:</p>
<dl>
<dt><a
href="http://epics-pvdata.sourceforge.net/docbuild/pvDataJava/tip/documentation/pvDataJava.html">pvData</a></dt>
<dd>pvData (Process Variable Data) defines and implements an efficent way
to store, access, and communicate memory resident structured data</dd>
<dt><a
href="http://epics-pvdata.sourceforge.net/docbuild/pvAccessJava/tip/documentation/pvAccessJava.html">pvAccess</a></dt>
<dd>pvAccess is a software library for high speed controls network communications,
optimized for pvData</dd>
<dt><a
href="http://epics-pvdata.sourceforge.net/docbuild/pvIOCJava/tip/documentation/pvIOCJava.html">pvIOC</a></dt>
<dd>pvIOC is a software framework for building network accessable "smart" real time
databases, suitable for interfacing devices in a distributed control system,
that can exchange pvData over pvAccess.
</dd>
<dt><a
href="http://epics-pvdata.sourceforge.net/docbuild/pvServiceJava/tip/documentation/pvAccessJava.html">pvService</a></dt>
<dd>A middle layer for implementing data services.</dd>
</dl>
<p>Each of these products has a Java and a C++ implementation.</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 27-Nov-2012 version of the definition of pvDatabaseCPP.
This is the original version.
</p>
<p>This is the beginning of the implementation of pvDataBaseCPP.
It describes the features that will be provided.
The class definitions for PVRecord and PVDatabase are defined but not implemented.</p>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
</div>
<div id="contents" class="contents">
<h2>Introduction</h2>
<h3>Overview</h3>
<p>This document descibes a C++ implementation of some of the components in pvIOCJava.
It extracts the core components required to create a network accessible database of smart
memory resident records.
pvDatabaseCPP does not and will not implement any of the specialized support that pvIOCJava
provides. Instead other projects will implement the specialized support.
It is expected that many services will be created that do not require the full features provided
by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of
them named pvDatabaseJava.
</p>
<p>A brief description of a pvDatase is that it is a network accessible set of smart memory resident
records. Each record has data composed of a top level PVStructure. Each record has a name which is
the channelName for pvAccess. A local Channel Provider implements the complete ChannelProvider and
Channel interfaces as defined by pvAccess.
This local provider is accessed by the remote pvAccess server.
A record is smart because code can be attached to a record.</p>
<p>This document describes components that provides the following features:
<dl>
<dt>database<dt>
<dd>This encapsulates the concept of a database of memory resident smart records.
The two main components are:
<dl>
<dt>pvRecord</dt>
<dd>This encapsulates the concept of a smart record. It can be processed.
Changes to field values can be trapped. A record can be locked.</dd>
<dt>pvDatabase<dt>
<dd>This is a database of pvRecords.
Records can be added and removed from a database.</dd>
</dl>
<dt>localChannelProvider</dt>
<dd>This is a complete implementation of ChannelProvider and Channel as defined by pvAccess.
It is used by the server side of pvAccess to attach to pvRecords.
This component also includes the monitor and pvCopy components from pvIOCJava</dd>
</dl>
<p><b>database</b> does not itself implement pvRecord instances.
Instead it provides a base classes that make it easy to create record instances.
What does have to be implemented is a top
level PVStructure and the following two methods:</p>
<dl>
<dt>process</dt>
<dd>This is what makes a record <b>smart</b>.
What process does is up to the implementation except that it must decide if
it's execution model is synchronous or asynchronous.
Synchronous means that when process returns the processing is complete.
Asynchronous means that when process returns the processing is <b>not</b> complete.
Instead process invokes other threads that will complete the processing at a later time.</dd>
<dt>isSynchronous</dt>
<dd>Which execution model is being implemented.</dd>
</dl>
<h3>Example PVRecord Extension</h3>
<p>Directory <b>example/record</b> has an example PVRecord implementation.
It implements a counter.
The top level structure is:</p>
<pre>
structure
long value
</pre>
<p><b>NOTE:</b> The example compiles but does not build because nothing
is implemented.</p>
<h4>exampleRecord.h</h4>
<p>This is the class description.
The example extends PVRecord.</p>
<pre>
class ExampleRecord :
public virtual PVRecord
{
public:
POINTER_DEFINITIONS(ExampleRecord);
static PVRecordPtr create(epics::pvData::String const &amp; recordName);
virtual ~ExampleRecord();
virtual bool isSynchronous();
virtual void process(
epics::pvDatabase::RecordProcessRequesterPtr const &amp;processRequester);
private:
ExampleRecord(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure,
epics::pvData::PVLongPtr const &amp;pvValue);
epics::pvData::PVLongPtr pvValue;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific. See the implemention for details.</dd>
<dt>~ExampleRecord<dt>
<dd>The destructor must be declared virtual.</dd>
<dt>isSynchronous<dt>
<dd>The implementation must say if process is synchronous or asynchronous.</dd>
<dt>process<dt>
<dd><b>The</b> implementation.</dd>
<dt>ExampleRecord<dt>
<dd>For the example this is private.</dd>
<dl>
<h4>exampleRecord.cpp</h4>
<p>This is the class implementation.</p>
<pre>
ExampleRecord::~ExampleRecord(){}
PVRecordPtr ExampleRecord::create(String const &amp; recordName)
{
String properties;
PVStructurePtr pvStructure = getStandardPVField()-&gt;scalar(pvLong,properties);
PVLongPtr pvValue = pvStructure-&gt;getLongField("value");
PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue));
return pvRecord;
}
ExampleRecord::ExampleRecord(
String const &amp; recordName,
PVStructurePtr const &amp; pvStructure,
PVLongPtr const &amp;pvValue)
: PVRecord(recordName,pvStructure),
pvValue(pvValue)
{}
bool ExampleRecord::isSynchronous() {return true;}
void ExampleRecord::process(
RecordProcessRequesterPtr const &amp;processRequester)
{
pvValue-&gt;put(pvValue-&gt;get() + 1);
processRequester-&gt;recordProcessResult(Status::Ok);
processRequester-&gt;recordProcessComplete();
}
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>Creates a PVStructure with a single subfield named value.
It gets the interface to the value field.
It then creates an ExampleRecord and returns it.
</dd>
<dt>~ExampleRecord<dt>
<dd>Does not have to do anything because of shared pointers.</dd>
<dt>ExampleRecord<dt>
<dd>Calls the base class constructor and sets pvValue.</dd>
<dt>isSynchronous<dt>
<dd>The example is synchronous.</dd>
<dt>process<dt>
<dd>Gets the curent value, increments it, and puts the new value.
It than calls two processRequester callbacks.</dd>
<dl>
<h4>exampleRecordMain.cpp</h4>
<p>This is a main for creating and running the example.</p>
<pre>
int main(int argc,char *argv[])
{
String recordName("exampleRecord");
PVRecordPtr pvRecord = ExampleRecord::create(recordName);
PVDatabasePtr pvDatabase = PVDatabase::getMaster();
pvDatabase-&gt;addRecord(pvRecord);
cout &lt;&lt; recordName &lt;&lt; "\n";
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}
</pre>
<p>The main program creates an example record and adds it to the database.
It then runs until the process is stopped by typing <b>exit</b>.
<p>Until the process is stopped,
pvAccess clients can put and get the value field.
For example</p>
<pre>
pvget exampleRecord
pvput exampleRecord 5
</pre>
<p>Will both work.</p>
<h3>Phased Development</h3>
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
<dl>
<dt>pvRecord</d>
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
<dt>pvDatabase</d>
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
<dt>Local Channel Provider</dt>
<dd>These two features will be the first phase.
But only synchronous record processing will be supported.</dd>
</dl>
<p>Future phases of pvDatabaseCPP should include:</p>
<dl>
<dt>Install</dt>
<dd>This provides on-line add and delete.</dd>
<dt>Field support</dt>
<dd>Add ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava will also be implemented.</dd>
<dt>XML parser</dt>
<dd>This provides the ability to create record instances without writing any code.</dd>
</dl>
<p>The completion of each phase provides useful features that can be used without waiting for the
completion of later phases.
The rest of this document discusses only the first phase.</p>
<h3>Features Required for localChannelProvider</h3>
<dl>
<dt>pvCopy</dt>
<dd>Creates a PVStructure that contains a copy of an arbitary
subset of the fields of another top level PVStructure.
It can copy data between the two and maintains a bitSet that show
which fields are changed.<dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to fields of a record.</dd>
<dt>PVRecord and PVDatabase</dt>
<dd>Defined below.</dd>
<dt>local ChannelProvider</dt>
<dd>This is the pvAccess package in pvIOCJava.
The localChannelProvider will access data from PVRecords.
It will implement all channel methods except channelRPC.</dd>
</dl>
<h3>Minumum Features Required for pvRecord</h3>
<p>The first phase will only implement record processing, i. e.
the process method has to do everything itself without any generic field support.
This will be sufficient for starting to implement services.
The following are the minimium features required</p>
<dl>
<dt>PVDatabase</dt>
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
<dt>PVRecord</dt>
<dd>This, and a set of related interfaces, provide the following:
<dl>
<dt>PVStructure</dt>
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
<dt>Record locking</dt>
<dd>A record can be locked and unlocked.
A record must be locked whenever data in the pvStructure is accessed.</dd>
<dt>Trapping data changes</dt>
<dd>A client can request to be notified when data in the pvStructure is modified.
It can do this on a field by field basis.</dd>
</dl>
</dd>
</dl>
<p>The following sections provide a first attempt to describe the classes required for the first
phase.</p>
<p>The last section gives a brief overview of the features provided by pvIOCJava.</p>
<h2>database</h2>
<p>The classes in <b>pvDatabase.h</b> implement a database of memory resident
smart records. The next subsection has the definitions for all the classes
defined in this header file.
It describes the following classes:</p>
<dl>
<dt>PVRecord</dt>
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
<dt>PVRecordField</dt>
<dt>PVRecordStructure</dt>
<dd>These <b>wrap</b> PVField and PVStructure so that pvCopy and monitor can be implemented.</dd>
<dt>PVListener</dt>
<dd>This is implemented by anything that wants to trap calls to the PVRecord::message.</dd>
<dt>RecordProcessRequester</dt>
<dd>This is implemented by anything that calls PVRecord::queueProcessRequest.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>PVDatabase</dt>
<dd>This is a database of PVRecords.</dd>
</dl>
<p>Each class is described in a separate subsection.</p>
<h3>class PVRecord</h3>
<pre>
class PVRecord
{
public:
POINTER_DEFINITIONS(PVRecord);
PVRecord(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void process(
RecordProcessRequesterPtr const &amp;recordProcessRequester) = 0;
virtual bool isSynchronous() = 0;
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField);
void lock();
void unlock();
void registerClient(PVRecordClientPtr const &amp; pvRecordClient);
void unregisterClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
void beginGroupPut();
void endGroupPut();
void registerListener(PVListenerPtr const &amp; pvListener);
void unregisterListener(PVListenerPtr const &amp; pvListener);
void removeEveryListener();
epics::pvData::Status processRequest();
void queueProcessRequest(
RecordProcessRequesterPtr const &amp;recordProcessRequester);
void addRequester(epics::pvData::RequesterPtr const &amp; requester);
void removeRequester(epics::pvData::RequesterPtr const &amp; requester);
void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
epics::pvData::String toString();
epics::pvData::String toString(int indentLevel);
};
</pre>
<p>The methods are:</h3>
<dl>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>~PVRecord</dt>
<dd>The desctructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
<dt>process</dt>
<dd>Pure virtual method. Derived classes must implement this method.</dd>
<dt>isSynchronous</dt>
<dd>Pure virtual method. Derived classes must implement this method.</dd>
<dt>getRecordName</dt>
<dd>Return the recordName.</dd>
<dt>getPVRecordStructure</dt>
<dd>Get the top level PVStructure.</dd>
<dt>findPVRecordField</dt>
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
<dt>lock</dt>
<dt>unlock</dt>
<dd>Lock and Unlock the record.
Any code accessing the data in the record or calling other PVRecord methods
must have the record locked.</dd>
<dt>registerClient</dt>
<dd>Every client that accesses the record must call this so that the client can be notified when the record is deleted.</dd>
<dt>unregisterClient</dt>
<dd>Client is no longer accessing the record.</dd>
<dt>detachClients</dt>
<dd>Ask all clients to detach from the record</dd>
<dt>beginGroupPut</dt>
<dd>Begin a group of puts. This results in all registered PVListeners being called</dd>
<dt>endGroupPut</dt>
<dd>End a group of puts. This results in all registered PVListeners being called.</dd>
<dt>registerListener</dt>
<dd>Register a PVListener. This must be called before calling pvRecordField.addListener.</dd>
<dt>unregisterListener</dt>
<dd>Unregister a listener. The listener will also be removed from all fields to which it is attached.</dd>
<dt>removeEveryListener</dt>
<dd>This must be called by any code that is deleting or changing the structure of a record.</dd>
<dt>processRequest</dt>
<dd>This is a convenience method for clients that are willing to block if
process is asynchronous. It implements RecordProcessRequester.
If process is synchronous it just calls process and returns the result
to the caller. If process is asynchronous it calls queueProcessRequest,
and process and waits for completion and then returns the result to the caller.</dd>
<dt>queueProcessRequest</dt>
<dd>Queue a process request.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
class PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField,
PVRecordPtr const &amp; pvRecord);
virtual ~PVRecordField();
PVRecordStructurePtr getParent();
epics::pvData::PVFieldPtr getPVField();
epics::pvData::String getFullFieldName();
epics::pvData::String getFullName();
PVRecordPtr getPVRecord();
bool addListener(PVListenerPtr const &amp; pvListener);
void removeListener(PVListenerPtr const &amp; pvListener);
void postPut();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
};
</pre>
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordField</dt>
<dd>The constructor.</dd>
<dt>~PVRecordField</dt>
<dd>The destructor.</dd>
<dt>getParent</dt>
<dd>Get the parent PVRecordStructure for this field.</dd>
<dt>getPVField</dt>
<dd>Get the PVField associated with this PVRecordField.</dd>
<dt>getFullFieldName</dt>
<dd>This gets the full name of the field, i.e. field,field,..</dd>
<dt>getFullName</dt>
<dd>This gets recordName plus the full name of the field, i.e. recordName.field,field,..</dd>
<dt>getPVRecord</dt>
<dd>Returns the PVRecord to which this field belongs.</dd>
<dt>addListener</dt>
<dd>Add A PVListener to this field.
Whenever this field or any subfield if this field is modified the listener will be notified.
PVListener is described below.
Before a listener can call addListener it must first call PVRecord.registerListener.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVRecordStructure</h3>
<pre>
class PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
PVRecordStructure(
epics::pvData::PVStructurePtr const &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField);
virtual ~PVRecordStructure();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
};
</pre>
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordStructure</dt>
<dd>The constructor.</dd>
<dt>~PVRecordStructure</dt>
<dd>The destructor.</dd>
<dt>getPVRecordFields</dt>
<dd>Get the PVRecordField array for the subfields</dd>
<dt>getPVStructure</dt>
<dd>Get the PVStructure for this field.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVListener</h3>
<pre>
class PVListener {
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener();
virtual void dataPut(PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void unlisten(PVRecordPtr const &amp; pvRecord) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~PVListener</dt>
<dd>The destructor.</dd>
<dt>dataPut(PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
This is called if the listener has called PVRecordField::addListener for pvRecordField.</dd>
<dt>dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
Requested is the field to which the requester issued a pvField-&amp;addListener.
This is called if the listener has called PVRecordField-&amp;addListener for requested.</dd>
<dt>beginGroupPut</dt>
<dd>A related set of changes is being started.</dd>
<dt>endGroupPut</dt>
<dd>A related set of changes is done.</dd>
<dt>unlisten</dt>
<dd>The PVLister is being removed from the record.
This is called when the record is being destroyed or when the record structure
(not the data values) is being changed.</dd>
</dl>
<h3>class RecordProcessRequester</h3>
<pre>
class RecordProcessRequester :
virtual public epics::pvData::Requester
{
public:
POINTER_DEFINITIONS(RecordProcessRequester);
virtual ~RecordProcessRequester();
virtual void becomeProcessor() = 0;
virtual void recordProcessResult(epics::pvData::Status status) = 0;
virtual void recordProcessComplete() = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~RecordProcessRequester</dt>
<dd>The destructor.</dd>
<dt>becomeProcessor</dt>
<dd>Called as a result of queueRequeProcessst. The requester can the call process.</dd>
<dt>recordProcessResult</dt>
<dd>The results of record processing.
This is called with the record locked so that the process requester
can access data from the record.</dd>
<dt>recordProcessComplete</dt>
<dd>Processing is complete.
This is called with the record unlocked.
If the process requester called process with leaveActive true then the requester
must call setInactive.</dd>
</dl>
<h3>class PVRecordClient</h3>
<pre>
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const &amp; pvRecord);
};
</pre>
<p>where</p>
<dl>
<dt>~PVRecordClient</dt>
<dd>The destructor.</dd>
<dt>detach</dt>
<dd>The record is being removed from the master database,</dd>
</dl>
<h3>class PVDatabase</h3>
<pre>
class PVDatabase : virtual public epics::pvData::Requester {
public:
POINTER_DEFINITIONS(PVDatabase);
static PVDatabasePtr getMaster();
virtual ~PVDatabase();
PVRecordPtr findRecord(epics::pvData::String const&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
private:
PVDatabase();
};
</pre>
<p>where</p>
<dl>
<dt>getMaster</dt>
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
<dt>~PVDatabase</dt>
<dd>The destructor.</dd>
<dt>findRecord</dt>
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
<dt>addRecord</dt>
<dd>Add a record to the database.
If the record already exists it is not modified and false is returned.</dd>
<dt>removeRecord</dt>
<dd>Remove a record from the database.
If the record was not in the database false is returned.</dd>
</dl>
<h2>Local Channel Provider</h2>
<p>Not yet described.</p>
<p>A brief description is that it must implement the following components of pvIOCJava:</p>
<dl>
<dt>pvCopy</dt>
<dt>monitor</dt>
<dt>pvAccess</dt>
<dd>See the next section for a description</dd>
</dl>
<h2>Summary of Packages in pvIOCJAVA</h2>
<p>The following are the direct sub packages of <b>pvIOCJava/src/org/epics/pvioc</b>:</p>
<dl>
<dt>pvCopy</dt>
<dd>This provides a copy of an arbitrary subset of the fields in a PVRecord.
It also provides the ability to detect and report changes to fields.
It is required for pvAccess.</dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to a PVRecord. It is required for pvAccess monitors.</dd>
<dt>pvAccess</dt>
<dd>The local implementation of Channel Provider and Channel.
It is accessed by the remote pvAccess server and can also be accessed by code in the same IOC.</dd>
<dt>database</dt>
<dd>This defines and implements PVRecord, PVDatabase , and PVListener.
It supports the basic feature required the implement a local Channel Provider.</dd>
<dt>support</dt>
<dd>This provides the ability to optionally attach code to any field of a pvRecord.
It and several sub packages provide a set of standard support modules.</dd>
<dt>install</dt>
<dd>This provides the ability to dynamically initialize and add new PVRecords. It also provides
the ability to dynamicall delete PVRecords.</d>
<dt>xml</dt>
<dd>This provides the ability to configure record instances without writing code.</dd>
<dt>util</dt>
<dd>This is misnamed since it is code related to scanning.</dd>
<dt>pdrv</dt>
<dd>This is portDriver, which is a proposed sucessor to the asynManager component of asynDriver.</dd>
<dt>swtshell</dt>
<dd>This is shell that is can either run under the same process as a JavaIOC or as a remote shell.
It is like a version of probe but for pvData/pvAccess.
Almost all of it's features work in either local or remote mode.
With a little more work all or it's features could work remotely.
This should be done and then only remote mode should be supported.
It can then be rewritten in a completely different language and using a complely different GUI
framework.
</dd>
<dt>caV3</dt>
<dd>This has two components:
<dl>
<dt>ClientFactory</dt>
<dd>This is a small wrapper on top of the caV3 client support implemented by pvAccess.
It allows code in the pvIOC to access V3Records via pvAccess.</dd>
<dt>ServerFactory</dt>
<dd>This is a caV3 server that allows a caV3 client to access a PVRecord.
The Java implementation uses CAJ, which does most of the work.
For now it will not be discussed in this document.</dd>
</dl>
</dd>
<dt>v3a</dt>
<dd>I do not know what this is.</dd>
</dl>
<p>In addition there is one class file <b>JavaIOC.java</b>.
This is starting a IOC instance.
This is not required for pvIOCCPP which is either a main or runs as part of a V3 IOC.</p>
</div>
</body>
</html>

View File

@@ -1,892 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>pvDatabaseCPP</title>
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/base.css" />
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
<style type="text/css">
/*<![CDATA[*/
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
table { margin-left: auto; margin-right: auto }
.diagram { text-align: center; margin: 2.5em 0 }
span.opt { color: grey }
span.nterm { font-style:italic }
span.term { font-family:courier }
span.user { font-family:courier }
span.user:before { content:"<" }
span.user:after { content:">" }
.nonnorm { font-style:italic }
p.ed { color: #AA0000 }
span.ed { color: #AA0000 }
p.ed.priv { display: inline; }
span.ed.priv { display: inline; }
/*]]>*/</style>
<!-- Script that generates the Table of Contents -->
<script type="text/javascript"
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
</script>
</head>
<body>
<div class="head">
<h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 11-Dec-2012</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>This version:</dt>
<dd><a
href="pvDatabaseCPP_20121211.html">pvDatabaseCPP20121211.html</a>
</dd>
<dt>Previous version:</dt>
<dd><a
href="pvDatabaseCPP_20121127.html">pvDatabaseCPP_20121127.html</a>
</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
</dl>
<p class="copyright">This product is made available subject to acceptance of the <a
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
<hr />
</div>
<h2 class="nocount">Abstract</h2>
<p>This document describes pvDatabaseCPP,
which is a framework for implementing a network accessable database of smart memory resident
records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by
pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess.
The framework must be extended in order to create record instances.
The minimum that an extenson must provide is a top level PVStructure and a process method
but the framework provides for complex extensions.</p>
<p>EPICS version 4 is a set of related products in the EPICS
V4 control system programming environment:</p>
<dl>
<dt><a
href="http://epics-pvdata.sourceforge.net/docbuild/pvDataJava/tip/documentation/pvDataJava.html">pvData</a></dt>
<dd>pvData (Process Variable Data) defines and implements an efficent way
to store, access, and communicate memory resident structured data</dd>
<dt><a
href="http://epics-pvdata.sourceforge.net/docbuild/pvAccessJava/tip/documentation/pvAccessJava.html">pvAccess</a></dt>
<dd>pvAccess is a software library for high speed controls network communications,
optimized for pvData</dd>
<dt><a
href="http://epics-pvdata.sourceforge.net/docbuild/pvIOCJava/tip/documentation/pvIOCJava.html">pvIOC</a></dt>
<dd>pvIOC is a software framework for building network accessable "smart" real time
databases, suitable for interfacing devices in a distributed control system,
that can exchange pvData over pvAccess.
</dd>
<dt><a
href="http://epics-pvdata.sourceforge.net/docbuild/pvServiceJava/tip/documentation/pvAccessJava.html">pvService</a></dt>
<dd>A middle layer for implementing data services.</dd>
</dl>
<p>Each of these products has a Java and a C++ implementation.</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 11-Dec-2012 version of the definition of pvDatabaseCPP.
</p>
<p>This is the beginning of the implementation of pvDataBaseCPP.
It describes the features that will be provided.
The class definitions for PVRecord are implemented.
The class definition for PVDatabase are defined but not implemented.</p>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
</div>
<div id="contents" class="contents">
<h2>Introduction</h2>
<h3>Overview</h3>
<p>This document descibes a C++ implementation of some of the components in pvIOCJava.
It extracts the core components required to create a network accessible database of smart
memory resident records.
pvDatabaseCPP does not and will not implement any of the specialized support that pvIOCJava
provides. Instead other projects will implement the specialized support.
It is expected that many services will be created that do not require the full features provided
by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of
them named pvDatabaseJava.
</p>
<p>A brief description of a pvDatase is that it is a network accessible set of smart memory resident
records. Each record has data composed of a top level PVStructure. Each record has a name which is
the channelName for pvAccess. A local Channel Provider implements the complete ChannelProvider and
Channel interfaces as defined by pvAccess.
This local provider is accessed by the remote pvAccess server.
A record is smart because code can be attached to a record.</p>
<p>This document describes components that provides the following features:
<dl>
<dt>database<dt>
<dd>This encapsulates the concept of a database of memory resident smart records.
The two main components are:
<dl>
<dt>pvRecord</dt>
<dd>This encapsulates the concept of a smart record. It can be processed.
Changes to field values can be trapped. A record can be locked.</dd>
<dt>pvDatabase<dt>
<dd>This is a database of pvRecords.
Records can be added and removed from a database.</dd>
</dl>
<dt>localChannelProvider</dt>
<dd>This is a complete implementation of ChannelProvider and Channel as defined by pvAccess.
It is used by the server side of pvAccess to attach to pvRecords.
This component also includes the monitor and pvCopy components from pvIOCJava</dd>
</dl>
<p><b>database</b> does not itself implement pvRecord instances.
Instead it provides a base classes that make it easy to create record instances.
What does have to be implemented is a top
level PVStructure and the following two methods:</p>
<dl>
<dt>process</dt>
<dd>This is what makes a record <b>smart</b>.
What process does is up to the implementation except that it must decide if
it's execution model is synchronous or asynchronous.
Synchronous means that when process returns the processing is complete.
Asynchronous means that when process returns the processing is <b>not</b> complete.
Instead process invokes other threads that will complete the processing at a later time.</dd>
<dt>isSynchronous</dt>
<dd>Which execution model is being implemented.</dd>
</dl>
<h3>Example PVRecord Extension</h3>
<p>Directory <b>example/record</b> has an example PVRecord implementation.
It implements a counter.
The top level structure is:</p>
<pre>
structure
long value
</pre>
<p><b>NOTE:</b> The example compiles but does not build because nothing
is implemented.</p>
<h4>exampleRecord.h</h4>
<p>This is the class description.
The example extends PVRecord.</p>
<pre>
class ExampleRecord :
public virtual PVRecord
{
public:
POINTER_DEFINITIONS(ExampleRecord);
static PVRecordPtr create(epics::pvData::String const &amp; recordName);
virtual ~ExampleRecord();
virtual bool isSynchronous();
virtual void process(
epics::pvDatabase::RecordProcessRequesterPtr const &amp;processRequester);
private:
ExampleRecord(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure,
epics::pvData::PVLongPtr const &amp;pvValue);
epics::pvData::PVLongPtr pvValue;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific. See the implemention for details.</dd>
<dt>~ExampleRecord<dt>
<dd>The destructor must be declared virtual.</dd>
<dt>isSynchronous<dt>
<dd>The implementation must say if process is synchronous or asynchronous.</dd>
<dt>process<dt>
<dd><b>The</b> implementation.</dd>
<dt>ExampleRecord<dt>
<dd>For the example this is private.</dd>
<dl>
<h4>exampleRecord.cpp</h4>
<p>This is the class implementation.</p>
<pre>
ExampleRecord::~ExampleRecord(){}
PVRecordPtr ExampleRecord::create(String const &amp; recordName)
{
String properties;
PVStructurePtr pvStructure = getStandardPVField()-&gt;scalar(pvLong,properties);
PVLongPtr pvValue = pvStructure-&gt;getLongField("value");
PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue));
return pvRecord;
}
ExampleRecord::ExampleRecord(
String const &amp; recordName,
PVStructurePtr const &amp; pvStructure,
PVLongPtr const &amp;pvValue)
: PVRecord(recordName,pvStructure),
pvValue(pvValue)
{}
bool ExampleRecord::isSynchronous() {return true;}
void ExampleRecord::process(
RecordProcessRequesterPtr const &amp;processRequester,bool alreadyLocked)
{
if(!alreadyLocked) lock();
pvValue-&gt;put(pvValue-&gt;get() + 1);
processRequester-&gt;recordProcessResult(Status::Ok);
unlock();
processRequester-&gt;recordProcessComplete();
dequeueProcessRequest(processRequester);
}
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>Creates a PVStructure with a single subfield named value.
It gets the interface to the value field.
It then creates an ExampleRecord and returns it.
</dd>
<dt>~ExampleRecord<dt>
<dd>Does not have to do anything because of shared pointers.</dd>
<dt>ExampleRecord<dt>
<dd>Calls the base class constructor and sets pvValue.</dd>
<dt>isSynchronous<dt>
<dd>The example is synchronous.</dd>
<dt>process<dt>
<dd>Gets the curent value, increments it, and puts the new value.
It than calls two processRequester callbacks.</dd>
<dl>
<h4>exampleRecordMain.cpp</h4>
<p>This is a main for creating and running the example.</p>
<pre>
int main(int argc,char *argv[])
{
String recordName("exampleRecord");
PVRecordPtr pvRecord = ExampleRecord::create(recordName);
PVDatabasePtr pvDatabase = PVDatabase::getMaster();
pvDatabase-&gt;addRecord(pvRecord);
cout &lt;&lt; recordName &lt;&lt; "\n";
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}
</pre>
<p>The main program creates an example record and adds it to the database.
It then runs until the process is stopped by typing <b>exit</b>.
<p>Until the process is stopped,
pvAccess clients can put and get the value field.
For example</p>
<pre>
pvget exampleRecord
pvput exampleRecord 5
</pre>
<p>Will both work.</p>
<h3>Phased Development</h3>
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
<dl>
<dt>pvRecord</d>
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
<dt>pvDatabase</d>
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
<dt>Local Channel Provider</dt>
<dd>These two features will be the first phase.
But only synchronous record processing will be supported.</dd>
</dl>
<p>Future phases of pvDatabaseCPP should include:</p>
<dl>
<dt>Install</dt>
<dd>This provides on-line add and delete.</dd>
<dt>Field support</dt>
<dd>Add ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava will also be implemented.</dd>
<dt>XML parser</dt>
<dd>This provides the ability to create record instances without writing any code.</dd>
</dl>
<p>The completion of each phase provides useful features that can be used without waiting for the
completion of later phases.
The rest of this document discusses only the first phase.</p>
<h3>Features Required for localChannelProvider</h3>
<dl>
<dt>pvCopy</dt>
<dd>Creates a PVStructure that contains a copy of an arbitary
subset of the fields of another top level PVStructure.
It can copy data between the two and maintains a bitSet that show
which fields are changed.<dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to fields of a record.</dd>
<dt>PVRecord and PVDatabase</dt>
<dd>Defined below.</dd>
<dt>local ChannelProvider</dt>
<dd>This is the pvAccess package in pvIOCJava.
The localChannelProvider will access data from PVRecords.
It will implement all channel methods except channelRPC.</dd>
</dl>
<h3>Minumum Features Required for pvRecord</h3>
<p>The first phase will only implement record processing, i. e.
the process method has to do everything itself without any generic field support.
This will be sufficient for starting to implement services.
The following are the minimium features required</p>
<dl>
<dt>PVDatabase</dt>
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
<dt>PVRecord</dt>
<dd>This, and a set of related interfaces, provide the following:
<dl>
<dt>PVStructure</dt>
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
<dt>Record locking</dt>
<dd>A record can be locked and unlocked.
A record must be locked whenever data in the pvStructure is accessed.</dd>
<dt>Trapping data changes</dt>
<dd>A client can request to be notified when data in the pvStructure is modified.
It can do this on a field by field basis.</dd>
</dl>
</dd>
</dl>
<p>The following sections provide a first attempt to describe the classes required for the first
phase.</p>
<p>The last section gives a brief overview of the features provided by pvIOCJava.</p>
<h2>database</h2>
<p>The classes in <b>pvDatabase.h</b> implement a database of memory resident
smart records.
It describes the following classes:</p>
<dl>
<dt>PVRecord</dt>
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
<dt>PVRecordField</dt>
<dt>PVRecordStructure</dt>
<dd>These <b>wrap</b> PVField and PVStructure so that pvCopy and monitor
can be implemented.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>PVListener</dt>
<dd>This is implemented by anything that wants to trap calls to the PVRecord::message.</dd>
<dt>RecordProcessRequester</dt>
<dd>This is implemented by anything that calls PVRecord::queueProcessRequest.</dd>
<dt>RecordPutRequester</dt>
<dd>This is implemented by anything that calls PVRecord::queuePutRequest.</dd>
<dt>PVDatabase</dt>
<dd>This is a database of PVRecords.</dd>
</dl>
<p>Each class is described in a separate subsection.</p>
<h3>C++ namespace and typedefs</h3>
<pre>
namespace epics { namespace pvDatabase {
class PVRecord;
typedef std::tr1::shared_ptr&lt;PVRecord&gt; PVRecordPtr;
class PVRecordField;
typedef std::tr1::shared_ptr&lt;PVRecordField&gt; PVRecordFieldPtr;
typedef std::vector&lt;PVRecordFieldPtr&gt; PVRecordFieldPtrArray;
typedef std::tr1::shared_ptr&lt;PVRecordFieldPtrArray&gt; PVRecordFieldPtrArrayPtr;
class PVRecordStructure;
typedef std::tr1::shared_ptr&lt;PVRecordStructure&gt; PVRecordStructurePtr;
class PVRecordClient;
typedef std::tr1::shared_ptr&lt;PVRecordClient&gt; PVRecordClientPtr;
class PVListener;
typedef std::tr1::shared_ptr&lt;PVListener&gt; PVListenerPtr;
class RecordProcessRequester;
typedef std::tr1::shared_ptr&lt;RecordProcessRequester&gt; RecordProcessRequesterPtr;
class RecordPutRequester;
typedef std::tr1::shared_ptr&lt;RecordPutRequester&gt; RecordPutRequesterPtr;
class PVDatabase;
typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
</pre>
<h3>class PVRecord</h3>
<p><b>NOTES:</b>
<ul>
<li>This section uses the name record instead of "an instance of PVRecord".</li>
<li>Most clients will access a record via the local channel provider,
i. e. via pvAccess.
Thus this section is mainly of interest to
the local channel provider and record implementers.</li>
</ul>
<hr>PVRecord Methods</h4>
<pre>
class PVRecord
public epics::pvData::Requester,
public std::tr1::enable_shared_from_this&lt;PVRecord&gt;
{
public:
POINTER_DEFINITIONS(PVRecord);
PVRecord(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void process(
RecordProcessRequesterPtr const &amp;recordProcessRequester,
bool alreadyLocked) = 0;
virtual bool isSynchronous() = 0;
virtual bool requestImmediatePut(epics::pvData::PVFieldPtr const &amp;pvField);
virtual void immediatePutDone();
virtual void destroy();
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField);
bool addRequester(epics::pvData::RequesterPtr const &amp; requester);
bool removeRequester(epics::pvData::RequesterPtr const &amp; requester);
void lock();
void unlock();
bool tryLock();
void lockOtherRecord(PVRecordPtr const &amp; otherRecord);
void addPVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void removePVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
void beginGroupPut();
void endGroupPut();
void queueProcessRequest(
RecordProcessRequesterPtr const &amp;recordProcessRequester);
void dequeueProcessRequest(
RecordProcessRequesterPtr const &amp;recordProcessRequester);
void queuePutRequest(
RecordPutRequesterPtr const &amp;recordPutRequester);
void putDone(
RecordPutRequesterPtr const &amp;recordPutRequester);
virtual epics::pvData::String getRequesterName();
void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void message(
PVRecordFieldPtr const &amp; pvRecordField,
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void toString(epics::pvData::StringBuilder buf);
void toString(epics::pvData::StringBuilder buf,int indentLevel);
//init MUST be called after derived class is constructed
void init();
};
</pre>
<p>The methods are:</h3>
<dl>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>~PVRecord</dt>
<dd>The destructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
<dt>process</dt>
<dd>Pure virtual method.
<p>Derived classes must implement this method.</p>
<p>A client <b>must</b> only call this method when
<b>RecordProcessRequester::becomeProcessor</b> is called as a result
of a <b>queueProcessRequest</b>.
A client can either call lock before calling processs
or let process lock the record.
If a client wants to put data into the record it should lock, put, and then call
process.</p>
<p>If the record is synchronous, process will return only when all processing
is complete. If the record is asynchronous then process arranges for some
other thread to do the processing and returns.</p>
<p>When processing is done the record calls two client callbacks:</p>
<dl>
<dt>RecordProcessRequester::recordProcessResult</dt>
<dd>This is called with the record still locked.
The clients can get data from the record.</dd>
<dt>RecordProcessRequester::recordProcessComplete</dt>
<dd>This is called with the record unlocked.
The client can no longer access the record.</dd>
</dl>
</dd>
<dt>isSynchronous</dt>
<dd>Pure virtual method. Derived classes must implement this method.</dd>
<dt>requestImmediatePut</dt>
<dd>This is a virtual method.
<p> The purpose is to allow the implementation to provide fields
that allow a client to abort process.
For example a motor record might provide a field <b>stop</b></p>
<p>The default always returns <b>false</b>.</p>
<p>A record implementation can override the default and return <b>true</b>.
In it does requestImmediatePut it returns with the record locked.</p>
<p>The client can change the value of the associated field and then call
<b>immediatePutDone</b></p>
</dd>
<dt>immediatePutDone</dt>
<dd>This is a virtual method.
<p>The default does nothing.</p>
<p>Must be called by client as a result of a call to <b>requestImmediatePut</b>
that returns <b>true</b>.</p>
</dd>
<dt>destroy</dt>
<dd>This is a virtual method.
<p>The default does nothing.</p>
</dd>
<dt>getRecordName</dt>
<dd>Return the recordName.</dd>
<dt>getPVRecordStructure</dt>
<dd>Get the top level PVStructure.</dd>
<dt>findPVRecordField</dt>
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>lock</dt>
<dt>unlock</dt>
<dd>Lock and Unlock the record.
Any code accessing the data in the record or calling other PVRecord methods
must have the record locked.</dd>
<dt>tryLock</dt>
<dd>If <b>true</b> then just like <b>lock</b>.
If <b>false</b>client can not access record.
A client can try to simultaneously hold the lock for more than two records
by calling this method. But must be willing to accept failure.
</dd>
<dt>lockOtherRecord</dt>
<dd>A client that holds the lock for one record can lock one other record.
A client <b>must</b> not call this if the client already has the lock for
more then one record.
</dd>
<dt>addPVRecordClient</dt>
<dd>Every client that accesses the record must call this so that the client can be notified when the record is deleted.</dd>
<dt>removePVRecordClient</dt>
<dd>Client is no longer accessing the record.</dd>
<dt>detachClients</dt>
<dd>Ask all clients to detach from the record</dd>
<dt>addListener</dt>
<dd>Add a PVListener. This must be called before calling pvRecordField.addListener.</dd>
<dt>removeListener</dt>
<dd>Removes a listener. The listener will also be removed from all fields to which it is attached.</dd>
<dt>beginGroupPut</dt>
<dd>Begin a group of puts.
This results in all registered PVListeners being called</dd>
<dt>endGroupPut</dt>
<dd>End a group of puts.
This results in all registered PVListeners being called.</dd>
<dt>queueProcessRequest</dt>
<dd>Queue a process request.</dd>
<dt>dequeueProcessRequest</dt>
<dd>This <b>must</b> be called by record implementation after it has
completed a process request.
</dd>
<dt>queuePutRequest</dt>
<dd>Queue a put request.
<p>This is for code that wants to change data in a record without processing.
If <b>RecordPutRequester::requestResult</b> is called with result <b>true</b>
then the record is locked and the client can make changes.
When done the client <b>must</b> call <b>putDone</b></p>
</dd>
<dt>putDone</dt>
<dd>Called by <b>RecordPutRequester</b> after changing values in record.
This method unlocks the record</dd>
<dt>getRequesterName</dt>
<dd>virtual method of <b>Requester</b>
</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
<dt>init</dt>
<dd>This method <b>must</b> be called by derived class
<b>after</b> class is completely constructed.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
class PVRecordField {
public virtual epics::pvData::PostHandler,
public std::tr1::enable_shared_from_this&lt;PVRecordField&gt;
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField,
PVRecordStructurePtr const &amp;parent,
PVRecordPtr const &amp; pvRecord);
virtual ~PVRecordField();
PVRecordStructurePtr getParent();
epics::pvData::PVFieldPtr getPVField();
epics::pvData::String getFullFieldName();
epics::pvData::String getFullName();
PVRecordPtr getPVRecord();
bool addListener(PVListenerPtr const &amp; pvListener);
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
};
</pre>
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordField</dt>
<dd>The constructor.</dd>
<dt>~PVRecordField</dt>
<dd>The destructor.</dd>
<dt>getParent</dt>
<dd>Get the parent PVRecordStructure for this field.</dd>
<dt>getPVField</dt>
<dd>Get the PVField associated with this PVRecordField.</dd>
<dt>getFullFieldName</dt>
<dd>This gets the full name of the field, i.e. field,field,..</dd>
<dt>getFullName</dt>
<dd>This gets recordName plus the full name of the field, i.e. recordName.field,field,..</dd>
<dt>getPVRecord</dt>
<dd>Returns the PVRecord to which this field belongs.</dd>
<dt>addListener</dt>
<dd>Add A PVListener to this field.
Whenever this field or any subfield if this field is modified the listener will be notified.
PVListener is described below.
Before a listener can call addListener it must first call PVRecord.registerListener.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVRecordStructure</h3>
<pre>
class PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
PVRecordStructure(
epics::pvData::PVStructurePtr const &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField);
virtual ~PVRecordStructure();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
};
</pre>
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordStructure</dt>
<dd>The constructor.</dd>
<dt>~PVRecordStructure</dt>
<dd>The destructor.</dd>
<dt>getPVRecordFields</dt>
<dd>Get the PVRecordField array for the subfields</dd>
<dt>getPVStructure</dt>
<dd>Get the PVStructure for this field.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<h3>class PVRecordClient</h3>
<pre>
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const &amp; pvRecord);
};
</pre>
<p>where</p>
<dl>
<dt>~PVRecordClient</dt>
<dd>The destructor.</dd>
<dt>detach</dt>
<dd>The record is being removed from the master database,</dd>
</dl>
</dl>
<h3>class PVListener</h3>
<pre>
class PVListener {
virtual public PVRecordClient
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener();
virtual void dataPut(PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~PVListener</dt>
<dd>The destructor.</dd>
<dt>dataPut(PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
This is called if the listener has called PVRecordField::addListener for pvRecordField.</dd>
<dt>dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
Requested is the field to which the requester issued a pvField-&amp;addListener.
This is called if the listener has called PVRecordField-&amp;addListener for requested.</dd>
<dt>beginGroupPut</dt>
<dd>A related set of changes is being started.</dd>
<dt>endGroupPut</dt>
<dd>A related set of changes is done.</dd>
</dl>
<h3>class RecordProcessRequester</h3>
<pre>
class RecordProcessRequester :
virtual public PVRecordClient,
virtual public epics::pvData::Requester
{
public:
POINTER_DEFINITIONS(RecordProcessRequester);
virtual ~RecordProcessRequester();
virtual void recordDestroyed() = 0;
virtual void becomeProcessor() = 0;
virtual void recordProcessResult(epics::pvData::Status status) = 0;
virtual void recordProcessComplete() = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~RecordProcessRequester</dt>
<dd>The destructor.</dd>
<dt>recordDestroyed</dt>
<dd>Record is being destroyed.</dd>
<dt>becomeProcessor</dt>
<dd>Called as a result of queueRequeProcessst. The requester can the call process.</dd>
<dt>recordProcessResult</dt>
<dd>The results of record processing.
This is called with the record locked so that the process requester
can access data from the record.</dd>
<dt>recordProcessComplete</dt>
<dd>Processing is complete.
This is called with the record unlocked.
If the process requester called process with leaveActive true then the requester
must call setInactive.</dd>
</dl>
<h3>class RecordPutRequester</h3>
<pre>
class RecordPutRequester :
virtual public PVRecordClient
{
public:
POINTER_DEFINITIONS(RecordPutRequester);
virtual ~RecordPutRequester();
virtual void requestResult(bool result) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~RecordPutRequester</dt>
<dd>The destructor.</dd>
<dt>requestResult</dt>
<dd>Result of a call to queuePutRequest. If <b>requestResult</b> is <b>false</b>
then the caller can not access the record.
If <b>requestResult</b> is <b>true</b>
then the record is locked and the caller can get and put data in the record.
When done the caller must call <b>PVRecord::putDone</b>, which will unlock the
record.
</dd>
</dl>
<h3>class PVDatabase</h3>
<pre>
class PVDatabase : virtual public epics::pvData::Requester {
public:
POINTER_DEFINITIONS(PVDatabase);
static PVDatabasePtr getMaster();
virtual ~PVDatabase();
PVRecordPtr findRecord(epics::pvData::String const&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
private:
PVDatabase();
};
</pre>
<p>where</p>
<dl>
<dt>getMaster</dt>
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
<dt>~PVDatabase</dt>
<dd>The destructor.</dd>
<dt>findRecord</dt>
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
<dt>addRecord</dt>
<dd>Add a record to the database.
If the record already exists it is not modified and false is returned.</dd>
<dt>removeRecord</dt>
<dd>Remove a record from the database.
If the record was not in the database false is returned.</dd>
</dl>
<h2>Local Channel Provider</h2>
<p>Not yet described.</p>
<p>A brief description is that it must implement the following components of pvIOCJava:</p>
<dl>
<dt>pvCopy</dt>
<dt>monitor</dt>
<dt>pvAccess</dt>
<dd>See the next section for a description</dd>
</dl>
<h2>Summary of Packages in pvIOCJAVA</h2>
<p>The following are the direct sub packages of <b>pvIOCJava/src/org/epics/pvioc</b>:</p>
<dl>
<dt>pvCopy</dt>
<dd>This provides a copy of an arbitrary subset of the fields in a PVRecord.
It also provides the ability to detect and report changes to fields.
It is required for pvAccess.</dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to a PVRecord. It is required for pvAccess monitors.</dd>
<dt>pvAccess</dt>
<dd>The local implementation of Channel Provider and Channel.
It is accessed by the remote pvAccess server and can also be accessed by code in the same IOC.</dd>
<dt>database</dt>
<dd>This defines and implements PVRecord, PVDatabase , and PVListener.
It supports the basic feature required the implement a local Channel Provider.</dd>
<dt>support</dt>
<dd>This provides the ability to optionally attach code to any field of a pvRecord.
It and several sub packages provide a set of standard support modules.</dd>
<dt>install</dt>
<dd>This provides the ability to dynamically initialize and add new PVRecords. It also provides
the ability to dynamicall delete PVRecords.</d>
<dt>xml</dt>
<dd>This provides the ability to configure record instances without writing code.</dd>
<dt>util</dt>
<dd>This is misnamed since it is code related to scanning.</dd>
<dt>pdrv</dt>
<dd>This is portDriver, which is a proposed sucessor to the asynManager component of asynDriver.</dd>
<dt>swtshell</dt>
<dd>This is shell that is can either run under the same process as a JavaIOC or as a remote shell.
It is like a version of probe but for pvData/pvAccess.
Almost all of it's features work in either local or remote mode.
With a little more work all or it's features could work remotely.
This should be done and then only remote mode should be supported.
It can then be rewritten in a completely different language and using a complely different GUI
framework.
</dd>
<dt>caV3</dt>
<dd>This has two components:
<dl>
<dt>ClientFactory</dt>
<dd>This is a small wrapper on top of the caV3 client support implemented by pvAccess.
It allows code in the pvIOC to access V3Records via pvAccess.</dd>
<dt>ServerFactory</dt>
<dd>This is a caV3 server that allows a caV3 client to access a PVRecord.
The Java implementation uses CAJ, which does most of the work.
For now it will not be discussed in this document.</dd>
</dl>
</dd>
<dt>v3a</dt>
<dd>I do not know what this is.</dd>
</dl>
<p>In addition there is one class file <b>JavaIOC.java</b>.
This is starting a IOC instance.
This is not required for pvIOCCPP which is either a main or runs as part of a V3 IOC.</p>
</div>
</body>
</html>

View File

@@ -1,865 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>pvDatabaseCPP</title>
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/base.css" />
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
<style type="text/css">
/*<![CDATA[*/
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
table { margin-left: auto; margin-right: auto }
.diagram { text-align: center; margin: 2.5em 0 }
span.opt { color: grey }
span.nterm { font-style:italic }
span.term { font-family:courier }
span.user { font-family:courier }
span.user:before { content:"<" }
span.user:after { content:">" }
.nonnorm { font-style:italic }
p.ed { color: #AA0000 }
span.ed { color: #AA0000 }
p.ed.priv { display: inline; }
span.ed.priv { display: inline; }
/*]]>*/</style>
<!-- Script that generates the Table of Contents -->
<script type="text/javascript"
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
</script>
</head>
<body>
<div class="head">
<h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 17-Apr-2013</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>This version:</dt>
<dd><a
href="pvDatabaseCPP_20130417.html">pvDatabaseCPP20130417.html</a>
</dd>
<dt>Previous version:</dt>
<dd><a
href="pvDatabaseCPP_20121211.html">pvDatabaseCPP20121211.html</a>
</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
</dl>
<p class="copyright">This product is made available subject to acceptance of the <a
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
<hr />
</div>
<h2 class="nocount">Abstract</h2>
<p>This document describes pvDatabaseCPP,
which is a framework for implementing a network accessable database of smart memory resident
records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by
pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess.
The framework can be extended in order to create record instances that implements services.
The minimum that an extenson must provide is a top level PVStructure and a process method.
</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 17-Apr-2013 version of the definition of pvDatabaseCPP.
</p>
<p>The following Channel methods are implemented and working: getField,i
channelProcess, channelGet, channelPut, and channelPutGet.
But lots of work remains:</p>
<dl>
<dt>Other Channel Methods</dt>
<dd>Monitor is next.</dd>
<dt>Memory leaks at exit</dt>
<dd>May need help from Matej.</dd>
<dt>Scalar Arrays</dt>
<dd>Have not been tested. Share has not been implemented.</dd>
<dt>Structure Arrays</dt>
<dd>Has not been implemented</dd>
<dt>Testing</dt>
<dd>Needs lots more testing</dd>
</dl>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
</div>
<div id="contents" class="contents">
<h2>Introduction</h2>
<h3>Overview</h3>
<p>A brief description of a pvDatabase is that it is a set of network accessible, smart,
memory resident records.
Each record has data composed of a top level PVStructure.
Each record has a name which is the channelName for pvAccess.
A local Channel Provider implements the complete ChannelProvider and
Channel interfaces as defined by pvAccess.
The local provider provides access to the records in the pvDatabase.
This local provider is accessed by the remote pvAccess server.
A record is smart because code can be attached to a record, which is accessed via a method named process.</p>
<p>This document describes components that provides the following features:
<dl>
<dt>database<dt>
<dd>This encapsulates the concept of a database of memory resident smart records.
The two main components are:
<dl>
<dt>pvRecord</dt>
<dd>This encapsulates the concept of a smart record. It can be processed.
Changes to field values can be trapped. A record can be locked.</dd>
<dt>pvDatabase<dt>
<dd>This is a database of pvRecords.
Records can be added and removed from a database.</dd>
</dl>
<dt>pvAccess</dt>
<dd>This is a complete implementation of ChannelProvider and Channel
as defined by pvAccess.
It is used by the server side of pvAccess to attach to pvRecords.
This component also includes the monitor and pvCopy components from pvIOCJava</dd>
</dl>
<p>database provides base classes that make it easy to create record instances.
The code attached to each record must create the top
level PVStructure and the following two methods:</p>
<dl>
<dt>init</dt>
<dd>This is a method for initializing the support.
It returns true if successful and false otherwise.
</dd>
<dt>process</dt>
<dd>This is what makes a record smart.
</dd>
</dl>
<h3>Relationship with pvIOCJava.</h3>
<p>This document descibes a C++ implementation of some of the components in pvIOCJava,
which also implements a pvDatabase.
PVDatabaseCPP extracts the core components required to create a network accessible database of smart
memory resident records.
pvDatabaseCPP does not implement any of the specialized support that pvIOCJava
provides.
It is expected that many services will be created that do not require the full features provided
by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of
them named pvDatabaseJava.</p>
<p>Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing.
For pvDatabaseCPP the process method is allowed to block.
Until a need is demonstrated this will remain true.
The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess.
The server side of remote pvAccess creates two threads for each client and always accesses
a record via these threads.
It is expected that these threads will be sufficient to efficently handle all channel methods except
channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests.
If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility,
then the scanning facility will have to provide some way of handling process requests that block.</p>
</p>
<h3>Example PVRecord Extension</h3>
<p>The example implements a simple counter.
The example can be run on linux as follows:</p>
<pre>
mrk&gt; pwd
/home/hg/pvDatabaseCPP
mrk&gt; bin/linux-x86_64/exampleCounter
</pre>
<p>The example consists of two components:</p>
<dl>
<dt>ExampleCounter.h</dt>
<dd>The source code for the counter.</dd>
<dt>exampleCounterMain.cpp</dt>
<dd>A main program that runs the example so that it can be accessed
by a pvAccess client.</dd>
</dl>
<h4>ExampleCounter.h</h4>
<p>The example resides in src/database.
The complete implementation is in the header file.
A serious implementation would probably break the code into two files:
1) a header, and 2) the implementation. The description consists of</p>
<pre>
class ExampleCounter;
typedef std::tr1::shared_ptr&lt;ExampleCounter&gt; ExampleCounterPtr;
class ExampleCounter :
public PVRecord
{
public:
POINTER_DEFINITIONS(ExampleCounter);
static ExampleCounterPtr create(
epics::pvData::String const &amp; recordName);
virtual ~ExampleCounter();
virtual void destroy();
virtual bool init();
virtual void process();
private:
ExampleCounter(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
epics::pvData::PVLongPtr pvValue;
epics::pvData::PVTimeStamp pvTimeStamp;
epics::pvData::TimeStamp timeStamp;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific but each support could provide
a similar static method.
</dd>
<dt>~ExampleCounter<dt>
<dd>The destructor must be declared virtual.</dd>
<dt><destroy</dt>
<dd>Called when the record is being destroyed.
This must call the base class destroy method.
<dt>init<dt>
<dd>A method to initialize the support. It returns true
if initialization is successful and false if not.
NOTE that this is a virtual method of PVRecord itself.</dd>
<dt>process<dt>
<dd>
This again is a virtual method of PVRecord.
</dd>
<dt>ExampleCounter<dt>
<dd>For the example this is private.</dd>
<dt>pvValue</dt>
<dd>This is the field of the top level structure that process
accesses.
</dd>
<dl>
<p>The implementation of create method is:</p>
<pre>
ExampleCounterPtr ExampleCounter::create(
epics::pvData::String const &amp; recordName)
{
epics::pvData::PVStructurePtr pvStructure =
epics::pvData::getStandardPVField()-&gt;scalar(
epics::pvData::pvDouble,"timeStamp,alarm"");
ExampleCounterPtr pvRecord(
new ExampleCounter(recordName,pvStructure));
if(!pvRecord-&gt;init()) pvRecord.reset();
return pvRecord;
}
</pre>
This:
<ul>
<li>Creates the top level structure.</li>
<li>Creates a ExampleCounterPtr via the constructor.</li>
<li>Calls init and if it fails resets the shared pointer.</li>
<li>Returns the shared pointer to the newly created record.</li>
</ul>
<p>The private constructor method is:</p>
<pre>
ExampleCounter::ExampleCounter(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure)
: PVRecord(recordName,pvStructure)
{
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
}
</pre>
The example is very simple. It just calls the base class constructor.
<p>The destructor and destroy methods are:</p>
<pre>
ExampleCounter::~ExampleCounter()
{
destroy();
}
void ExampleCounter::destroy()
{
PVRecord::destroy();
}
</pre>
The destructor just calls destroy.
The destroy method, which is virtual, just calls the destroy method of the base class.
A more complicated example can clean up any resources it used but must call the base
class destroy method.
<p>The implementation of init is:</p>
<pre>
bool ExampleCounter::init()
{
initPVRecord();
epics::pvData::PVFieldPtr pvField;
pvValue = getPVStructure()-&gt;getLongField("value");
if(pvValue.get()==NULL) return false;
return true;
}
</pre>
This:
<ul>
<li>Calls initRecord which is implemented by the base class.
It MUST be called.</li>
<li>Calls getLongField to get the interface to the value field,
which must be a scalar with type long.</li>
<li>If a long value field was not found it returns false.</li>
<li>Returns true</li>
</ul>
<p>The implementation of process is:</p>
<pre>
void ExampleCounter::process()
{
pvValue-&gt;put(pvValue-&gt;get() + 1.0);
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
}
</pre>
It adds 1.0 to the current value.
It then sets the timeStamp to the current time.
<h4>exampleCounterMain.cpp</h4>
<p>This is in test/server.
The main program is:</p>
<pre>
int main(int argc,char *argv[])
{
PVDatabasePtr master = PVDatabase::getMaster();
ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
String recordName("exampleCounter");
PVRecordPtr pvRecord = ExampleCounter::create(recordName);
bool result = master-&gt;addRecord(pvRecord);
cout &lt;&lt; "result of addRecord " &lt;&lt; recordName &lt;&lt; " " &lt;&lt; result &lt;&lt; endl;
pvRecord.reset();
cout &lt;&lt; "exampleServer\n";
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}
</pre>
This:
<ul>
<li>Gets a pointer to the master database.</li>
<li>Creates the local Channel Provider. This starts the pvAccess server.</li>
<li>Creates a ExampleCounter record with the name exampleCounter
</li>
<li>Prints exampleCounter on standard out.</li>
<li>Runs forever until the user types exit on standard in.</li>
</ul>
<h3>Phased Development</h3>
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
<dl>
<dt>pvRecord</d>
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
<dt>pvDatabase</d>
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
<dt>Local Channel Provider</dt>
<dd>Complete implementation of ChannelProvider and Channel.
This means that pvCopy and monitor are also implemented.</dd>
</dl>
<p>Future phases of pvDatabaseCPP might include:</p>
<dl>
<dt>Install</dt>
<dd>This provides complete support for on-line add and delete
of sets of records.
With the first phase each "service" is responsible for it's own implementation.
All that is provided is addRecord and removeRecord.
</dd>
<dt>Field support</dt>
<dd>Add the ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava could also be implemented.</dd>
<dt>XML parser</dt>
<dd>This provides the ability to create record instances without writing any code.</dd>
</dl>
<p>The completion of each phase provides useful features that can be used without waiting for the
completion of later phases.
The rest of this document discusses only the first phase.</p>
<h3>Features Required for localChannelProvider</h3>
<dl>
<dt>pvCopy</dt>
<dd>Creates a PVStructure that contains a copy of an arbitary
subset of the fields of another top level PVStructure.
It can copy data between the two and maintains a bitSet that show
which fields are changed.<dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to fields of a record.</dd>
<dt>PVRecord and PVDatabase</dt>
<dd>Defined below.</dd>
<dt>The localChannelProvider itself</dt>
<dd>This is the pvAccess package in pvIOCJava.
The localChannelProvider will access data from PVRecords.
It will implement all channel methods.</dd>
</dl>
<h3>Minumum Features Required for pvRecord</h3>
<p>The first phase will only implement record processing, i. e.
the process method has to do everything itself without any generic field support.
This will be sufficient for starting to implement services.
The following are the minimium features required</p>
<dl>
<dt>PVDatabase</dt>
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
<dt>PVRecord</dt>
<dd>This, and a set of related interfaces, provides the following:
<dl>
<dt>Access to top level PVStructure</dt>
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
<dt>Record locking</dt>
<dd>A record can be locked and unlocked.
A record must be locked whenever data in the pvStructure is accessed.</dd>
<dt>Trapping data changes</dt>
<dd>A client can request to be notified when data in the pvStructure is modified.
It can do this on a field by field basis.</dd>
</dl>
</dd>
</dl>
<p>The following sections describes the classes required for the first phase.</p>
<h2>database</h2>
<p>This directory has the following files:</p>
<dl>
<dt>pvDatabase.h</dt>
<dd>
This is what is described in this section.
</dd>
<dt>pvDatabase.cpp</dt>
<dd>
The implementation of PVDatabase.
</dd>
<dt>pvRecord.cpp</dt>
<dd>
The implementation of the base class for PVREcord.
It can also implement record instances with a process
method does nothing.
This can be used to create a "dumb" record where all changes are
done by clients.
The complete implementation is provided in the header file.
Thus code will be generated only if other code includes the
header file and creates a record instance.
</dd>
<dt>exampleCounter.h</dt>
<dd>
This was described in the introduction.
</dd>
<dt>powerSupplyRecordTest.h</dt>
<dd>
This provides code that simulates a power supply.
It computes the current from the voltage and power.
It is used for testing.
The complete implementation is provided in the header file.
Thus code will be generated only if other code includes the
header file and creates a record instance.
</dd>
</dl>
<p>The classes in pvDatabase.h describe a database of memory resident
smart records.
It describes the following classes:</p>
<dl>
<dt>PVRecord</dt>
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
<dt>PVRecordField</dt>
<dt>PVRecordStructure</dt>
<dd>These wrap PVField and PVStructure so that pvCopy and monitor
can be implemented.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>PVListener</dt>
<dd>This is implemented by anything that wants to trap calls to PVRecord::message.</dd>
<dt>PVDatabase</dt>
<dd>This is a database of PVRecords.</dd>
</dl>
<p>Each class is described in a separate subsection.</p>
<h3>C++ namespace and typedefs</h3>
<pre>
namespace epics { namespace pvDatabase {
class PVRecord;
typedef std::tr1::shared_ptr&lt;PVRecord&gt; PVRecordPtr;
typedef std::map&lt;epics::pvData::String,PVRecordPtr&gt; PVRecordMap;
class PVRecordField;
typedef std::tr1::shared_ptr&lt;PVRecordField&gt; PVRecordFieldPtr;
typedef std::vector&lt;PVRecordFieldPtr&gt; PVRecordFieldPtrArray;
typedef std::tr1::shared_ptr&lt;PVRecordFieldPtrArray&gt; PVRecordFieldPtrArrayPtr;
class PVRecordStructure;
typedef std::tr1::shared_ptr&lt;PVRecordStructure&gt; PVRecordStructurePtr;
class PVRecordClient;
typedef std::tr1::shared_ptr&lt;PVRecordClient&gt; PVRecordClientPtr;
class PVListener;
typedef std::tr1::shared_ptr&lt;PVListener&gt; PVListenerPtr;
class RecordProcessRequester;
typedef std::tr1::shared_ptr&lt;RecordProcessRequester&gt; RecordProcessRequesterPtr;
class RecordPutRequester;
typedef std::tr1::shared_ptr&lt;RecordPutRequester&gt; RecordPutRequesterPtr;
class PVDatabase;
typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
</pre>
<h3>class PVRecord</h3>
<p>NOTES:
<ul>
<li>This section uses the name record instead of "an instance of PVRecord".</li>
<li>Most clients will access a record via the local channel provider,
i. e. via pvAccess.
Thus this section is mainly of interest to
the local channel provider and record implementers.</li>
<li>Most readers will not care about most of the PVRecord methods.
Most of the methods are used by the pvAccess code.
Service implementers will mostly be interested in methods init and process.
These are described first.
</ul>
<hr>PVRecord Methods</h4>
<pre>
class PVRecord
public epics::pvData::Requester,
public std::tr1::enable_shared_from_this&lt;PVRecord&gt;
{
public:
POINTER_DEFINITIONS(PVRecord);
virtual bool init() {initPVRecord(); return true;}
virtual void process() {}
static PVRecordPtr create(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void destroy();
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField);
bool addRequester(epics::pvData::RequesterPtr const &amp; requester);
bool removeRequester(epics::pvData::RequesterPtr const &amp; requester);
inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
void lock();
void unlock();
bool tryLock();
void lockOtherRecord(PVRecordPtr const &amp; otherRecord);
bool addPVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
bool removePVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
bool addListener(PVListenerPtr const &amp; pvListener);
bool removeListener(PVListenerPtr const &amp; pvListener);
void beginGroupPut();
void endGroupPut();
epics::pvData::String getRequesterName() {return getRecordName();}
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void message(
PVRecordFieldPtr const &amp; pvRecordField,
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void toString(epics::pvData::StringBuilder buf);
void toString(epics::pvData::StringBuilder buf,int indentLevel);
protected:
PVRecord(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
void initPVRecord();
epics::pvData::PVStructurePtr getPVStructure();
PVRecordPtr getPtrSelf()
{
return shared_from_this();
}
private:
...
}
</pre>
<p>The methods are:</h3>
<dl>
<dt>init</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
This method Must call initPVRecord.</p>
</dd>
<dt>process</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
The base implementation does nothing.</p>
</dd>
<dt>create</dt>
<dd>Static method to create dumb records, i.e. records with a process method
that does nothing.</dd>
<dt>~PVRecord</dt>
<dd>The destructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
<dt>destroy</dt>
<dd>This is a virtual method.
</dd>
<dt>getRecordName</dt>
<dd>Return the recordName.</dd>
<dt>getPVRecordStructure</dt>
<dd>Get the top level PVStructure.</dd>
<dt>findPVRecordField</dt>
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>lock_guard</dt>
<dd>This is an inline method that locks the record. The record will automatically
be unlocked when control leaves the block that has the call.
<dt>lock</dt>
<dt>unlock</dt>
<dd>Lock and Unlock the record.
Any code accessing the data in the record or calling other PVRecord methods
must have the record locked.</dd>
<dt>tryLock</dt>
<dd>If true then just like lock.
If falseclient can not access record.
A client can try to simultaneously hold the lock for more than two records
by calling this method. But must be willing to accept failure.
</dd>
<dt>lockOtherRecord</dt>
<dd>A client that holds the lock for one record can lock one other record.
A client must not call this if the client already has the lock for
more then one record.
</dd>
<dt>addPVRecordClient</dt>
<dd>Every client that accesses the record must call this so that the client can be notified when the record is deleted.</dd>
<dt>removePVRecordClient</dt>
<dd>Client is no longer accessing the record.</dd>
<dt>detachClients</dt>
<dd>Ask all clients to detach from the record</dd>
<dt>addListener</dt>
<dd>Add a PVListener. This must be called before calling pvRecordField.addListener.</dd>
<dt>removeListener</dt>
<dd>Removes a listener. The listener will also be removed from all fields to which it is attached.</dd>
<dt>beginGroupPut</dt>
<dd>Begin a group of puts.
This results in all registered PVListeners being called</dd>
<dt>endGroupPut</dt>
<dd>End a group of puts.
This results in all registered PVListeners being called.</dd>
<dt>getRequesterName</dt>
<dd>virtual method of Requester
</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
<dt>toString</dt>
<dd>Just calls the top level PVStructure toString method.</dd>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>initPVRecord</dt>
<dd>This method must be called by derived class.</dd>
<dt>getPVStructure</dt>
<dd>Called by derived class.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
class PVRecordField {
public virtual epics::pvData::PostHandler,
public std::tr1::enable_shared_from_this&lt;PVRecordField&gt;
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField,
PVRecordStructurePtr const &amp;parent,
PVRecordPtr const &amp; pvRecord);
virtual ~PVRecordField();
virtual void destroy();
PVRecordStructurePtr getParent();
epics::pvData::PVFieldPtr getPVField();
epics::pvData::String getFullFieldName();
epics::pvData::String getFullName();
PVRecordPtr getPVRecord();
bool addListener(PVListenerPtr const &amp; pvListener);
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
protected:
PVRecordFieldPtr getPtrSelf()
{
return shared_from_this();
}
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordField</dt>
<dd>The constructor.</dd>
<dt>~PVRecordField</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>Called by PVRecordStructure when it's destroy method is called.</dd>
<dt>getParent</dt>
<dd>Get the parent PVRecordStructure for this field.</dd>
<dt>getPVField</dt>
<dd>Get the PVField associated with this PVRecordField.</dd>
<dt>getFullFieldName</dt>
<dd>This gets the full name of the field, i.e. field,field,..</dd>
<dt>getFullName</dt>
<dd>This gets recordName plus the full name of the field, i.e. recordName.field,field,..</dd>
<dt>getPVRecord</dt>
<dd>Returns the PVRecord to which this field belongs.</dd>
<dt>addListener</dt>
<dd>Add A PVListener to this field.
Whenever this field or any subfield if this field is modified the listener will be notified.
PVListener is described below.
Before a listener can call addListener it must first call PVRecord.registerListener.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVRecordStructure</h3>
<pre>
class PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
PVRecordStructure(
epics::pvData::PVStructurePtr const &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField);
virtual ~PVRecordStructure();
virtual void destroy();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
protected:
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordStructure</dt>
<dd>The constructor.</dd>
<dt>~PVRecordStructure</dt>
<dd>The destructor.</dd>
<dt>getPVRecordFields</dt>
<dd>Get the PVRecordField array for the subfields</dd>
<dt>getPVStructure</dt>
<dd>Get the PVStructure for this field.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<h3>class PVRecordClient</h3>
<pre>
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const &amp; pvRecord);
};
</pre>
<p>where</p>
<dl>
<dt>~PVRecordClient</dt>
<dd>The destructor.</dd>
<dt>detach</dt>
<dd>The record is being removed from the master database,</dd>
</dl>
</dl>
<h3>class PVListener</h3>
<pre>
class PVListener {
virtual public PVRecordClient
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener();
virtual void dataPut(PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~PVListener</dt>
<dd>The destructor.</dd>
<dt>dataPut(PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
This is called if the listener has called PVRecordField::addListener for pvRecordField.</dd>
<dt>dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
Requested is the field to which the requester issued a pvField-&amp;addListener.
This is called if the listener has called PVRecordField-&amp;addListener for requested.</dd>
<dt>beginGroupPut</dt>
<dd>A related set of changes is being started.</dd>
<dt>endGroupPut</dt>
<dd>A related set of changes is done.</dd>
</dl>
<h3>class PVDatabase</h3>
<pre>
class PVDatabase : virtual public epics::pvData::Requester {
public:
POINTER_DEFINITIONS(PVDatabase);
static PVDatabasePtr getMaster();
virtual ~PVDatabase();
virtual void destroy();
PVRecordPtr findRecord(epics::pvData::String const&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
virtual epics::pvData::String getRequesterName();
virtual void message(
epics::pvData::String const &amp;message,
epics::pvData::MessageType messageType);
private:
PVDatabase();
};
</pre>
<p>where</p>
<dl>
<dt>getMaster</dt>
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
<dt>~PVDatabase</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>This is called by remote channelAccess when process exits.
This destroys and removes all records in the PVDatabase.</dd>
<dt>findRecord</dt>
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
<dt>addRecord</dt>
<dd>Add a record to the database.
If the record already exists it is not modified and false is returned.</dd>
<dt>removeRecord</dt>
<dd>Remove a record from the database.
If the record was not in the database false is returned.</dd>
<dt>getRequesterName</dt>
<dd>Virtual method of Requester</dd>
<dt>message</dt>
<dd>Virtual message of Requester.</dd>
</dl>
<h2>pvAccess</h2>
<p>Not yet described.
It is only of interest to someone who wants to understand how it works.
</p>
<p>A brief description is that it implements the following components of pvIOCJava:</p>
<dl>
<dt>pvCopy</dt>
<dt>monitor</dt>
<dt>local ChannelProvider and Channel</dt>
</dl>
</div>
</body>
</html>

View File

@@ -1,871 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>pvDatabaseCPP</title>
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/base.css" />
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
<style type="text/css">
/*<![CDATA[*/
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
table { margin-left: auto; margin-right: auto }
.diagram { text-align: center; margin: 2.5em 0 }
span.opt { color: grey }
span.nterm { font-style:italic }
span.term { font-family:courier }
span.user { font-family:courier }
span.user:before { content:"<" }
span.user:after { content:">" }
.nonnorm { font-style:italic }
p.ed { color: #AA0000 }
span.ed { color: #AA0000 }
p.ed.priv { display: inline; }
span.ed.priv { display: inline; }
/*]]>*/</style>
<!-- Script that generates the Table of Contents -->
<script type="text/javascript"
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
</script>
</head>
<body>
<div class="head">
<h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 16-May-2013</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>This version:</dt>
<dd><a
href="pvDatabaseCPP_20130516.html">pvDatabaseCPP20130516.html</a>
</dd>
<dt>Previous version:</dt>
<dd><a
href="pvDatabaseCPP_20130417.html">pvDatabaseCPP20130417.html</a>
</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
</dl>
<p class="copyright">This product is made available subject to acceptance of the <a
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
<hr />
</div>
<h2 class="nocount">Abstract</h2>
<p>This document describes pvDatabaseCPP,
which is a framework for implementing a network accessable database of smart memory resident
records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by
pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess.
The framework can be extended in order to create record instances that implements services.
The minimum that an extenson must provide is a top level PVStructure and a process method.
</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 16-May-2013 version of the definition of pvDatabaseCPP.
</p>
<p>The following Channel methods are implemented and working: getField,
channelProcess, channelGet, channelPut, channelPutGet, and Monitor.
But lots of work remains:</p>
<dl>
<dt>Other Channel Methods</dt>
<dd>ChannelArray is next.</dd>
<dt>Monitor Algorithms</dt>
<dd>Monitor algorithms have no been implemented.
Thus all monitors are onPut.</dd>
<dt>Lifecycle problems</dt>
<dd>Problems when channel clients disconnect.
May need help from Matej</dd>
<dt>Memory leaks at exit</dt>
<dd>May need help from Matej.</dd>
<dt>Scalar Arrays</dt>
<dd>Have not been tested. Share has not been implemented.</dd>
<dt>Structure Arrays</dt>
<dd>Has not been implemented</dd>
<dt>Testing</dt>
<dd>Needs lots more testing</dd>
</dl>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
</div>
<div id="contents" class="contents">
<h2>Introduction</h2>
<h3>Overview</h3>
<p>A brief description of a pvDatabase is that it is a set of network accessible, smart,
memory resident records.
Each record has data composed of a top level PVStructure.
Each record has a name which is the channelName for pvAccess.
A local Channel Provider implements the complete ChannelProvider and
Channel interfaces as defined by pvAccess.
The local provider provides access to the records in the pvDatabase.
This local provider is accessed by the remote pvAccess server.
A record is smart because code can be attached to a record, which is accessed via a method named process.</p>
<p>This document describes components that provides the following features:
<dl>
<dt>database<dt>
<dd>This encapsulates the concept of a database of memory resident smart records.
The two main components are:
<dl>
<dt>pvRecord</dt>
<dd>This encapsulates the concept of a smart record. It can be processed.
Changes to field values can be trapped. A record can be locked.</dd>
<dt>pvDatabase<dt>
<dd>This is a database of pvRecords.
Records can be added and removed from a database.</dd>
</dl>
<dt>pvAccess</dt>
<dd>This is a complete implementation of ChannelProvider and Channel
as defined by pvAccess.
It is used by the server side of pvAccess to attach to pvRecords.
This component also includes the monitor and pvCopy components from pvIOCJava</dd>
</dl>
<p>database provides base classes that make it easy to create record instances.
The code attached to each record must create the top
level PVStructure and the following two methods:</p>
<dl>
<dt>init</dt>
<dd>This is a method for initializing the support.
It returns true if successful and false otherwise.
</dd>
<dt>process</dt>
<dd>This is what makes a record smart.
</dd>
</dl>
<h3>Relationship with pvIOCJava.</h3>
<p>This document descibes a C++ implementation of some of the components in pvIOCJava,
which also implements a pvDatabase.
PVDatabaseCPP extracts the core components required to create a network accessible database of smart
memory resident records.
pvDatabaseCPP does not implement any of the specialized support that pvIOCJava
provides.
It is expected that many services will be created that do not require the full features provided
by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of
them named pvDatabaseJava.</p>
<p>Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing.
For pvDatabaseCPP the process method is allowed to block.
Until a need is demonstrated this will remain true.
The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess.
The server side of remote pvAccess creates two threads for each client and always accesses
a record via these threads.
It is expected that these threads will be sufficient to efficently handle all channel methods except
channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests.
If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility,
then the scanning facility will have to provide some way of handling process requests that block.</p>
</p>
<h3>Example PVRecord Extension</h3>
<p>The example implements a simple counter.
The example can be run on linux as follows:</p>
<pre>
mrk&gt; pwd
/home/hg/pvDatabaseCPP
mrk&gt; bin/linux-x86_64/exampleCounter
</pre>
<p>The example consists of two components:</p>
<dl>
<dt>ExampleCounter.h</dt>
<dd>The source code for the counter.</dd>
<dt>exampleCounterMain.cpp</dt>
<dd>A main program that runs the example so that it can be accessed
by a pvAccess client.</dd>
</dl>
<h4>ExampleCounter.h</h4>
<p>The example resides in src/database.
The complete implementation is in the header file.
A serious implementation would probably break the code into two files:
1) a header, and 2) the implementation. The description consists of</p>
<pre>
class ExampleCounter;
typedef std::tr1::shared_ptr&lt;ExampleCounter&gt; ExampleCounterPtr;
class ExampleCounter :
public PVRecord
{
public:
POINTER_DEFINITIONS(ExampleCounter);
static ExampleCounterPtr create(
epics::pvData::String const &amp; recordName);
virtual ~ExampleCounter();
virtual void destroy();
virtual bool init();
virtual void process();
private:
ExampleCounter(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
epics::pvData::PVLongPtr pvValue;
epics::pvData::PVTimeStamp pvTimeStamp;
epics::pvData::TimeStamp timeStamp;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific but each support could provide
a similar static method.
</dd>
<dt>~ExampleCounter<dt>
<dd>The destructor must be declared virtual.</dd>
<dt><destroy</dt>
<dd>Called when the record is being destroyed.
This must call the base class destroy method.
<dt>init<dt>
<dd>A method to initialize the support. It returns true
if initialization is successful and false if not.
NOTE that this is a virtual method of PVRecord itself.</dd>
<dt>process<dt>
<dd>
This again is a virtual method of PVRecord.
</dd>
<dt>ExampleCounter<dt>
<dd>For the example this is private.</dd>
<dt>pvValue</dt>
<dd>This is the field of the top level structure that process
accesses.
</dd>
<dl>
<p>The implementation of create method is:</p>
<pre>
ExampleCounterPtr ExampleCounter::create(
epics::pvData::String const &amp; recordName)
{
epics::pvData::PVStructurePtr pvStructure =
epics::pvData::getStandardPVField()-&gt;scalar(
epics::pvData::pvDouble,"timeStamp,alarm"");
ExampleCounterPtr pvRecord(
new ExampleCounter(recordName,pvStructure));
if(!pvRecord-&gt;init()) pvRecord.reset();
return pvRecord;
}
</pre>
This:
<ul>
<li>Creates the top level structure.</li>
<li>Creates a ExampleCounterPtr via the constructor.</li>
<li>Calls init and if it fails resets the shared pointer.</li>
<li>Returns the shared pointer to the newly created record.</li>
</ul>
<p>The private constructor method is:</p>
<pre>
ExampleCounter::ExampleCounter(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure)
: PVRecord(recordName,pvStructure)
{
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
}
</pre>
The example is very simple. It just calls the base class constructor.
<p>The destructor and destroy methods are:</p>
<pre>
ExampleCounter::~ExampleCounter()
{
destroy();
}
void ExampleCounter::destroy()
{
PVRecord::destroy();
}
</pre>
The destructor just calls destroy.
The destroy method, which is virtual, just calls the destroy method of the base class.
A more complicated example can clean up any resources it used but must call the base
class destroy method.
<p>The implementation of init is:</p>
<pre>
bool ExampleCounter::init()
{
initPVRecord();
epics::pvData::PVFieldPtr pvField;
pvValue = getPVStructure()-&gt;getLongField("value");
if(pvValue.get()==NULL) return false;
return true;
}
</pre>
This:
<ul>
<li>Calls initRecord which is implemented by the base class.
It MUST be called.</li>
<li>Calls getLongField to get the interface to the value field,
which must be a scalar with type long.</li>
<li>If a long value field was not found it returns false.</li>
<li>Returns true</li>
</ul>
<p>The implementation of process is:</p>
<pre>
void ExampleCounter::process()
{
pvValue-&gt;put(pvValue-&gt;get() + 1.0);
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
}
</pre>
It adds 1.0 to the current value.
It then sets the timeStamp to the current time.
<h4>exampleCounterMain.cpp</h4>
<p>This is in test/server.
The main program is:</p>
<pre>
int main(int argc,char *argv[])
{
PVDatabasePtr master = PVDatabase::getMaster();
ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
String recordName("exampleCounter");
PVRecordPtr pvRecord = ExampleCounter::create(recordName);
bool result = master-&gt;addRecord(pvRecord);
cout &lt;&lt; "result of addRecord " &lt;&lt; recordName &lt;&lt; " " &lt;&lt; result &lt;&lt; endl;
pvRecord.reset();
cout &lt;&lt; "exampleServer\n";
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}
</pre>
This:
<ul>
<li>Gets a pointer to the master database.</li>
<li>Creates the local Channel Provider. This starts the pvAccess server.</li>
<li>Creates a ExampleCounter record with the name exampleCounter
</li>
<li>Prints exampleCounter on standard out.</li>
<li>Runs forever until the user types exit on standard in.</li>
</ul>
<h3>Phased Development</h3>
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
<dl>
<dt>pvRecord</d>
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
<dt>pvDatabase</d>
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
<dt>Local Channel Provider</dt>
<dd>Complete implementation of ChannelProvider and Channel.
This means that pvCopy and monitor are also implemented.</dd>
</dl>
<p>Future phases of pvDatabaseCPP might include:</p>
<dl>
<dt>Install</dt>
<dd>This provides complete support for on-line add and delete
of sets of records.
With the first phase each "service" is responsible for it's own implementation.
All that is provided is addRecord and removeRecord.
</dd>
<dt>Field support</dt>
<dd>Add the ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava could also be implemented.</dd>
<dt>XML parser</dt>
<dd>This provides the ability to create record instances without writing any code.</dd>
</dl>
<p>The completion of each phase provides useful features that can be used without waiting for the
completion of later phases.
The rest of this document discusses only the first phase.</p>
<h3>Features Required for localChannelProvider</h3>
<dl>
<dt>pvCopy</dt>
<dd>Creates a PVStructure that contains a copy of an arbitary
subset of the fields of another top level PVStructure.
It can copy data between the two and maintains a bitSet that show
which fields are changed.<dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to fields of a record.</dd>
<dt>PVRecord and PVDatabase</dt>
<dd>Defined below.</dd>
<dt>The localChannelProvider itself</dt>
<dd>This is the pvAccess package in pvIOCJava.
The localChannelProvider will access data from PVRecords.
It will implement all channel methods.</dd>
</dl>
<h3>Minumum Features Required for pvRecord</h3>
<p>The first phase will only implement record processing, i. e.
the process method has to do everything itself without any generic field support.
This will be sufficient for starting to implement services.
The following are the minimium features required</p>
<dl>
<dt>PVDatabase</dt>
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
<dt>PVRecord</dt>
<dd>This, and a set of related interfaces, provides the following:
<dl>
<dt>Access to top level PVStructure</dt>
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
<dt>Record locking</dt>
<dd>A record can be locked and unlocked.
A record must be locked whenever data in the pvStructure is accessed.</dd>
<dt>Trapping data changes</dt>
<dd>A client can request to be notified when data in the pvStructure is modified.
It can do this on a field by field basis.</dd>
</dl>
</dd>
</dl>
<p>The following sections describes the classes required for the first phase.</p>
<h2>database</h2>
<p>This directory has the following files:</p>
<dl>
<dt>pvDatabase.h</dt>
<dd>
This is what is described in this section.
</dd>
<dt>pvDatabase.cpp</dt>
<dd>
The implementation of PVDatabase.
</dd>
<dt>pvRecord.cpp</dt>
<dd>
The implementation of the base class for PVREcord.
It can also implement record instances with a process
method does nothing.
This can be used to create a "dumb" record where all changes are
done by clients.
The complete implementation is provided in the header file.
Thus code will be generated only if other code includes the
header file and creates a record instance.
</dd>
<dt>exampleCounter.h</dt>
<dd>
This was described in the introduction.
</dd>
<dt>powerSupplyRecordTest.h</dt>
<dd>
This provides code that simulates a power supply.
It computes the current from the voltage and power.
It is used for testing.
The complete implementation is provided in the header file.
Thus code will be generated only if other code includes the
header file and creates a record instance.
</dd>
</dl>
<p>The classes in pvDatabase.h describe a database of memory resident
smart records.
It describes the following classes:</p>
<dl>
<dt>PVRecord</dt>
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
<dt>PVRecordField</dt>
<dt>PVRecordStructure</dt>
<dd>These wrap PVField and PVStructure so that pvCopy and monitor
can be implemented.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>PVListener</dt>
<dd>This is implemented by anything that wants to trap calls to PVRecord::message.</dd>
<dt>PVDatabase</dt>
<dd>This is a database of PVRecords.</dd>
</dl>
<p>Each class is described in a separate subsection.</p>
<h3>C++ namespace and typedefs</h3>
<pre>
namespace epics { namespace pvDatabase {
class PVRecord;
typedef std::tr1::shared_ptr&lt;PVRecord&gt; PVRecordPtr;
typedef std::map&lt;epics::pvData::String,PVRecordPtr&gt; PVRecordMap;
class PVRecordField;
typedef std::tr1::shared_ptr&lt;PVRecordField&gt; PVRecordFieldPtr;
typedef std::vector&lt;PVRecordFieldPtr&gt; PVRecordFieldPtrArray;
typedef std::tr1::shared_ptr&lt;PVRecordFieldPtrArray&gt; PVRecordFieldPtrArrayPtr;
class PVRecordStructure;
typedef std::tr1::shared_ptr&lt;PVRecordStructure&gt; PVRecordStructurePtr;
class PVRecordClient;
typedef std::tr1::shared_ptr&lt;PVRecordClient&gt; PVRecordClientPtr;
class PVListener;
typedef std::tr1::shared_ptr&lt;PVListener&gt; PVListenerPtr;
class RecordProcessRequester;
typedef std::tr1::shared_ptr&lt;RecordProcessRequester&gt; RecordProcessRequesterPtr;
class RecordPutRequester;
typedef std::tr1::shared_ptr&lt;RecordPutRequester&gt; RecordPutRequesterPtr;
class PVDatabase;
typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
</pre>
<h3>class PVRecord</h3>
<p>NOTES:
<ul>
<li>This section uses the name record instead of "an instance of PVRecord".</li>
<li>Most clients will access a record via the local channel provider,
i. e. via pvAccess.
Thus this section is mainly of interest to
the local channel provider and record implementers.</li>
<li>Most readers will not care about most of the PVRecord methods.
Most of the methods are used by the pvAccess code.
Service implementers will mostly be interested in methods init and process.
These are described first.
</ul>
<hr>PVRecord Methods</h4>
<pre>
class PVRecord
public epics::pvData::Requester,
public std::tr1::enable_shared_from_this&lt;PVRecord&gt;
{
public:
POINTER_DEFINITIONS(PVRecord);
virtual bool init() {initPVRecord(); return true;}
virtual void process() {}
static PVRecordPtr create(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void destroy();
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField);
bool addRequester(epics::pvData::RequesterPtr const &amp; requester);
bool removeRequester(epics::pvData::RequesterPtr const &amp; requester);
inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
void lock();
void unlock();
bool tryLock();
void lockOtherRecord(PVRecordPtr const &amp; otherRecord);
bool addPVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
bool removePVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
bool addListener(PVListenerPtr const &amp; pvListener);
bool removeListener(PVListenerPtr const &amp; pvListener);
void beginGroupPut();
void endGroupPut();
epics::pvData::String getRequesterName() {return getRecordName();}
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void message(
PVRecordFieldPtr const &amp; pvRecordField,
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void toString(epics::pvData::StringBuilder buf);
void toString(epics::pvData::StringBuilder buf,int indentLevel);
protected:
PVRecord(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
void initPVRecord();
epics::pvData::PVStructurePtr getPVStructure();
PVRecordPtr getPtrSelf()
{
return shared_from_this();
}
private:
...
}
</pre>
<p>The methods are:</h3>
<dl>
<dt>init</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
This method Must call initPVRecord.</p>
</dd>
<dt>process</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
The base implementation does nothing.</p>
</dd>
<dt>create</dt>
<dd>Static method to create dumb records, i.e. records with a process method
that does nothing.</dd>
<dt>~PVRecord</dt>
<dd>The destructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
<dt>destroy</dt>
<dd>This is a virtual method.
</dd>
<dt>getRecordName</dt>
<dd>Return the recordName.</dd>
<dt>getPVRecordStructure</dt>
<dd>Get the top level PVStructure.</dd>
<dt>findPVRecordField</dt>
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>lock_guard</dt>
<dd>This is an inline method that locks the record. The record will automatically
be unlocked when control leaves the block that has the call.
<dt>lock</dt>
<dt>unlock</dt>
<dd>Lock and Unlock the record.
Any code accessing the data in the record or calling other PVRecord methods
must have the record locked.</dd>
<dt>tryLock</dt>
<dd>If true then just like lock.
If falseclient can not access record.
A client can try to simultaneously hold the lock for more than two records
by calling this method. But must be willing to accept failure.
</dd>
<dt>lockOtherRecord</dt>
<dd>A client that holds the lock for one record can lock one other record.
A client must not call this if the client already has the lock for
more then one record.
</dd>
<dt>addPVRecordClient</dt>
<dd>Every client that accesses the record must call this so that the client can be notified when the record is deleted.</dd>
<dt>removePVRecordClient</dt>
<dd>Client is no longer accessing the record.</dd>
<dt>detachClients</dt>
<dd>Ask all clients to detach from the record</dd>
<dt>addListener</dt>
<dd>Add a PVListener. This must be called before calling pvRecordField.addListener.</dd>
<dt>removeListener</dt>
<dd>Removes a listener. The listener will also be removed from all fields to which it is attached.</dd>
<dt>beginGroupPut</dt>
<dd>Begin a group of puts.
This results in all registered PVListeners being called</dd>
<dt>endGroupPut</dt>
<dd>End a group of puts.
This results in all registered PVListeners being called.</dd>
<dt>getRequesterName</dt>
<dd>virtual method of Requester
</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
<dt>toString</dt>
<dd>Just calls the top level PVStructure toString method.</dd>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>initPVRecord</dt>
<dd>This method must be called by derived class.</dd>
<dt>getPVStructure</dt>
<dd>Called by derived class.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
class PVRecordField {
public virtual epics::pvData::PostHandler,
public std::tr1::enable_shared_from_this&lt;PVRecordField&gt;
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField,
PVRecordStructurePtr const &amp;parent,
PVRecordPtr const &amp; pvRecord);
virtual ~PVRecordField();
virtual void destroy();
PVRecordStructurePtr getParent();
epics::pvData::PVFieldPtr getPVField();
epics::pvData::String getFullFieldName();
epics::pvData::String getFullName();
PVRecordPtr getPVRecord();
bool addListener(PVListenerPtr const &amp; pvListener);
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
protected:
PVRecordFieldPtr getPtrSelf()
{
return shared_from_this();
}
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordField</dt>
<dd>The constructor.</dd>
<dt>~PVRecordField</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>Called by PVRecordStructure when it's destroy method is called.</dd>
<dt>getParent</dt>
<dd>Get the parent PVRecordStructure for this field.</dd>
<dt>getPVField</dt>
<dd>Get the PVField associated with this PVRecordField.</dd>
<dt>getFullFieldName</dt>
<dd>This gets the full name of the field, i.e. field,field,..</dd>
<dt>getFullName</dt>
<dd>This gets recordName plus the full name of the field, i.e. recordName.field,field,..</dd>
<dt>getPVRecord</dt>
<dd>Returns the PVRecord to which this field belongs.</dd>
<dt>addListener</dt>
<dd>Add A PVListener to this field.
Whenever this field or any subfield if this field is modified the listener will be notified.
PVListener is described below.
Before a listener can call addListener it must first call PVRecord.registerListener.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVRecordStructure</h3>
<pre>
class PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
PVRecordStructure(
epics::pvData::PVStructurePtr const &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField);
virtual ~PVRecordStructure();
virtual void destroy();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
protected:
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordStructure</dt>
<dd>The constructor.</dd>
<dt>~PVRecordStructure</dt>
<dd>The destructor.</dd>
<dt>getPVRecordFields</dt>
<dd>Get the PVRecordField array for the subfields</dd>
<dt>getPVStructure</dt>
<dd>Get the PVStructure for this field.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<h3>class PVRecordClient</h3>
<pre>
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const &amp; pvRecord);
};
</pre>
<p>where</p>
<dl>
<dt>~PVRecordClient</dt>
<dd>The destructor.</dd>
<dt>detach</dt>
<dd>The record is being removed from the master database,</dd>
</dl>
</dl>
<h3>class PVListener</h3>
<pre>
class PVListener {
virtual public PVRecordClient
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener();
virtual void dataPut(PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~PVListener</dt>
<dd>The destructor.</dd>
<dt>dataPut(PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
This is called if the listener has called PVRecordField::addListener for pvRecordField.</dd>
<dt>dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
Requested is the field to which the requester issued a pvField-&amp;addListener.
This is called if the listener has called PVRecordField-&amp;addListener for requested.</dd>
<dt>beginGroupPut</dt>
<dd>A related set of changes is being started.</dd>
<dt>endGroupPut</dt>
<dd>A related set of changes is done.</dd>
</dl>
<h3>class PVDatabase</h3>
<pre>
class PVDatabase : virtual public epics::pvData::Requester {
public:
POINTER_DEFINITIONS(PVDatabase);
static PVDatabasePtr getMaster();
virtual ~PVDatabase();
virtual void destroy();
PVRecordPtr findRecord(epics::pvData::String const&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
virtual epics::pvData::String getRequesterName();
virtual void message(
epics::pvData::String const &amp;message,
epics::pvData::MessageType messageType);
private:
PVDatabase();
};
</pre>
<p>where</p>
<dl>
<dt>getMaster</dt>
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
<dt>~PVDatabase</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>This is called by remote channelAccess when process exits.
This destroys and removes all records in the PVDatabase.</dd>
<dt>findRecord</dt>
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
<dt>addRecord</dt>
<dd>Add a record to the database.
If the record already exists it is not modified and false is returned.</dd>
<dt>removeRecord</dt>
<dd>Remove a record from the database.
If the record was not in the database false is returned.</dd>
<dt>getRequesterName</dt>
<dd>Virtual method of Requester</dd>
<dt>message</dt>
<dd>Virtual message of Requester.</dd>
</dl>
<h2>pvAccess</h2>
<p>Not yet described.
It is only of interest to someone who wants to understand how it works.
</p>
<p>A brief description is that it implements the following components of pvIOCJava:</p>
<dl>
<dt>pvCopy</dt>
<dt>monitor</dt>
<dt>local ChannelProvider and Channel</dt>
</dl>
</div>
</body>
</html>

View File

@@ -1,996 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>pvDatabaseCPP</title>
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/base.css" />
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
<style type="text/css">
/*<![CDATA[*/
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
table { margin-left: auto; margin-right: auto }
.diagram { text-align: center; margin: 2.5em 0 }
span.opt { color: grey }
span.nterm { font-style:italic }
span.term { font-family:courier }
span.user { font-family:courier }
span.user:before { content:"<" }
span.user:after { content:">" }
.nonnorm { font-style:italic }
p.ed { color: #AA0000 }
span.ed { color: #AA0000 }
p.ed.priv { display: inline; }
span.ed.priv { display: inline; }
/*]]>*/</style>
<!-- Script that generates the Table of Contents -->
<script type="text/javascript"
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
</script>
</head>
<body>
<div class="head">
<h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 23-May-2013</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>This version:</dt>
<dd><a
href="pvDatabaseCPP_20130523.html">pvDatabaseCPP20130523.html</a>
</dd>
<dt>Previous version:</dt>
<dd><a
href="pvDatabaseCPP_20130516.html">pvDatabaseCPP20130516.html</a>
</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
</dl>
<p class="copyright">This product is made available subject to acceptance of the <a
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
<hr />
</div>
<h2 class="nocount">Abstract</h2>
<p>This document describes pvDatabaseCPP,
which is a framework for implementing a network accessable database of smart memory resident
records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by
pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess.
The framework can be extended in order to create record instances that implements services.
The minimum that an extenson must provide is a top level PVStructure and a process method.
</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 23-May-2013 version of the definition of pvDatabaseCPP.
</p>
<p>The following Channel methods are implemented and working: getField,
channelProcess, channelGet, channelPut, channelPutGet, and Monitor.
But lots of work remains:</p>
<dl>
<dt>Other Channel Methods</dt>
<dd>Only ChannelArray remains.
Note that pvIOCJava does not implement the pvRequest for channelArray
correctly. It uses a private convention rather than using the output
of CreateRequest. This will be fixed before pvDatabaseCPP implements ChannelArray.
</dd>
<dt>Monitor Algorithms</dt>
<dd>Monitor algorithms have not been implemented.
Thus all monitors are onPut.</dd>
<dt>Lifecycle problems</dt>
<dd>Problems when channel clients disconnect.
I am asking for help from Matej</dd>
<dt>Memory leak at exit</dt>
<dd>I am asking for help from Matej</dd>
<dt>Scalar Arrays</dt>
<dd>Share has not been implemented.
This will wait until Michael has new implementation of ScalarArray.</dd>
<dt>Structure Arrays</dt>
<dd>Has not been implemented</dd>
<dt>Testing</dt>
<dd>Needs lots more testing</dd>
</dl>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
</div>
<div id="contents" class="contents">
<h2>Introduction</h2>
<h3>Overview</h3>
<p>A brief description of a pvDatabase is that it is a set of network accessible, smart,
memory resident records.
Each record has data composed of a top level PVStructure.
Each record has a name which is the channelName for pvAccess.
A local Channel Provider implements the complete ChannelProvider and
Channel interfaces as defined by pvAccess.
The local provider provides access to the records in the pvDatabase.
This local provider is accessed by the remote pvAccess server.
A record is smart because code can be attached to a record, which is accessed via a method named process.</p>
<p>This document describes components that provide the following features:
<dl>
<dt>database<dt>
<dd>This encapsulates the concept of a database of memory resident smart records.
The two main components are:
<dl>
<dt>pvRecord</dt>
<dd>This encapsulates the concept of a smart record. It can be processed.
Changes to field values can be trapped. A record can be locked.</dd>
<dt>pvDatabase<dt>
<dd>This is a database of pvRecords.
Records can be added and removed from a database.</dd>
</dl>
<dt>pvAccess</dt>
<dd>This is a complete implementation of ChannelProvider and Channel
as defined by pvAccess.
It is used by the server side of pvAccess to attach to pvRecords.
This component also includes the monitor and pvCopy components from pvIOCJava</dd>
</dl>
<p>database provides base classes that make it easy to create record instances.
The code attached to each record must create the top
level PVStructure and the following two methods:</p>
<dl>
<dt>init</dt>
<dd>This is a method for initializing the support.
It returns true if successful and false otherwise.
</dd>
<dt>process</dt>
<dd>This is what makes a record smart.
</dd>
</dl>
<h3>Relationship with pvIOCJava.</h3>
<p>This document descibes a C++ implementation of some of the components in pvIOCJava,
which also implements a pvDatabase.
PVDatabaseCPP extracts the core components required to create a network accessible database of smart
memory resident records.
pvDatabaseCPP does not implement any of the specialized support that pvIOCJava
provides.
It is expected that many services will be created that do not require the full features provided
by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of
them named pvDatabaseJava.</p>
<p>Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing.
For pvDatabaseCPP the process method is allowed to block.
Until a need is demonstrated this will remain true.
The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess.
The server side of remote pvAccess creates two threads for each client and always accesses
a record via these threads.
It is expected that these threads will be sufficient to efficently handle all channel methods except
channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests.
If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility,
then the scanning facility will have to provide some way of handling process requests that block.</p>
</p>
<h3>Example PVRecord Extension</h3>
<p>The example implements a simple counter.
The example can be run on linux as follows:</p>
<pre>
mrk&gt; pwd
/home/hg/pvDatabaseCPP
mrk&gt; bin/linux-x86_64/exampleCounter
</pre>
<p>The example consists of two components:</p>
<dl>
<dt>ExampleCounter.h</dt>
<dd>The source code for the counter.</dd>
<dt>exampleCounterMain.cpp</dt>
<dd>A main program that runs the example so that it can be accessed
by a pvAccess client.</dd>
</dl>
<h4>ExampleCounter.h</h4>
<p>The example resides in src/database.
The complete implementation is in the header file.
A serious implementation might break the code into a header and an
implementation file.<p>
</p>The description consists of</p>
<pre>
class ExampleCounter;
typedef std::tr1::shared_ptr&lt;ExampleCounter&gt; ExampleCounterPtr;
class ExampleCounter :
public PVRecord
{
public:
POINTER_DEFINITIONS(ExampleCounter);
static ExampleCounterPtr create(
epics::pvData::String const &amp; recordName);
virtual ~ExampleCounter();
virtual void destroy();
virtual bool init();
virtual void process();
private:
ExampleCounter(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
epics::pvData::PVLongPtr pvValue;
epics::pvData::PVTimeStamp pvTimeStamp;
epics::pvData::TimeStamp timeStamp;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific but each support could provide
a similar static method.
</dd>
<dt>~ExampleCounter<dt>
<dd>The destructor must be declared virtual.</dd>
<dt><destroy</dt>
<dd>Called when the record is being destroyed.
This must call the base class destroy method.
<dt>init<dt>
<dd>A method to initialize the support. It returns true
if initialization is successful and false if not.
NOTE that this is a virtual method of PVRecord itself.</dd>
<dt>process<dt>
<dd>
This again is a virtual method of PVRecord.
</dd>
<dt>ExampleCounter<dt>
<dd>For the example this is private.</dd>
<dt>pvValue</dt>
<dd>This is the field of the top level structure that process
accesses.
</dd>
<dl>
<p>The implementation of create method is:</p>
<pre>
ExampleCounterPtr ExampleCounter::create(
epics::pvData::String const &amp; recordName)
{
epics::pvData::PVStructurePtr pvStructure =
epics::pvData::getStandardPVField()-&gt;scalar(
epics::pvData::pvDouble,"timeStamp,alarm"");
ExampleCounterPtr pvRecord(
new ExampleCounter(recordName,pvStructure));
if(!pvRecord-&gt;init()) pvRecord.reset();
return pvRecord;
}
</pre>
This:
<ul>
<li>Creates the top level structure.</li>
<li>Creates a ExampleCounterPtr via the constructor.</li>
<li>Calls init and if it fails resets the shared pointer.</li>
<li>Returns the shared pointer to the newly created record.</li>
</ul>
<p>The private constructor method is:</p>
<pre>
ExampleCounter::ExampleCounter(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure)
: PVRecord(recordName,pvStructure)
{
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
}
</pre>
The example is very simple. It just calls the base class constructor.
<p>The destructor and destroy methods are:</p>
<pre>
ExampleCounter::~ExampleCounter()
{
destroy();
}
void ExampleCounter::destroy()
{
PVRecord::destroy();
}
</pre>
The destructor just calls destroy.
The destroy method, which is virtual, just calls the destroy method of the base class.
A more complicated example can clean up any resources it used but must call the base
class destroy method.
<p>The implementation of init is:</p>
<pre>
bool ExampleCounter::init()
{
initPVRecord();
epics::pvData::PVFieldPtr pvField;
pvValue = getPVStructure()-&gt;getLongField("value");
if(pvValue.get()==NULL) return false;
return true;
}
</pre>
This:
<ul>
<li>Calls initRecord which is implemented by the base class.
It MUST be called.</li>
<li>Calls getLongField to get the interface to the value field,
which must be a scalar with type long.</li>
<li>If a long value field was not found it returns false.</li>
<li>Returns true</li>
</ul>
<p>The implementation of process is:</p>
<pre>
void ExampleCounter::process()
{
pvValue-&gt;put(pvValue-&gt;get() + 1.0);
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
}
</pre>
It adds 1.0 to the current value.
It then sets the timeStamp to the current time.
<h4>exampleCounterMain.cpp</h4>
<p>This is in test/server.
The main program is:</p>
<pre>
int main(int argc,char *argv[])
{
PVDatabasePtr master = PVDatabase::getMaster();
ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
String recordName("exampleCounter");
PVRecordPtr pvRecord = ExampleCounter::create(recordName);
bool result = master-&gt;addRecord(pvRecord);
cout &lt;&lt; "result of addRecord " &lt;&lt; recordName &lt;&lt; " " &lt;&lt; result &lt;&lt; endl;
pvRecord.reset();
cout &lt;&lt; "exampleServer\n";
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}
</pre>
This:
<ul>
<li>Gets a pointer to the master database.</li>
<li>Creates the local Channel Provider. This starts the pvAccess server.</li>
<li>Creates a ExampleCounter record with the name exampleCounter
</li>
<li>Prints exampleCounter on standard out.</li>
<li>Runs forever until the user types exit on standard in.</li>
</ul>
<h3>Phased Development</h3>
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
<dl>
<dt>pvRecord</d>
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
<dt>pvDatabase</d>
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
<dt>Local Channel Provider</dt>
<dd>Complete implementation of ChannelProvider and Channel.
This means that pvCopy and monitor are also implemented.</dd>
</dl>
<p>Future phases of pvDatabaseCPP might include:</p>
<dl>
<dt>Install</dt>
<dd>This provides complete support for on-line add and delete
of sets of records.
With the first phase each "service" is responsible for it's own implementation.
All that is provided is addRecord and removeRecord.
</dd>
<dt>Field support</dt>
<dd>Add the ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava could also be implemented.</dd>
<dt>XML parser</dt>
<dd>This provides the ability to create record instances without writing any code.</dd>
</dl>
<p>The completion of each phase provides useful features that can be used without waiting for the
completion of later phases.
The rest of this document discusses only the first phase.</p>
<h3>Features Required for localChannelProvider</h3>
<dl>
<dt>pvCopy</dt>
<dd>Creates a PVStructure that contains a copy of an arbitary
subset of the fields of another top level PVStructure.
It can copy data between the two and maintains a bitSet that show
which fields are changed.<dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to fields of a record.</dd>
<dt>PVRecord and PVDatabase</dt>
<dd>Defined below.</dd>
<dt>The localChannelProvider itself</dt>
<dd>This is the pvAccess package in pvIOCJava.
The localChannelProvider will access data from PVRecords.
It will implement all channel methods.</dd>
</dl>
<h3>Minumum Features Required for pvRecord</h3>
<p>The first phase will only implement record processing, i. e.
the process method has to do everything itself without any generic field support.
This will be sufficient for starting to implement services.
The following are the minimium features required</p>
<dl>
<dt>PVDatabase</dt>
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
<dt>PVRecord</dt>
<dd>This, and a set of related interfaces, provides the following:
<dl>
<dt>Access to top level PVStructure</dt>
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
<dt>Record locking</dt>
<dd>A record can be locked and unlocked.
A record must be locked whenever data in the pvStructure is accessed.</dd>
<dt>Trapping data changes</dt>
<dd>A client can request to be notified when data in the pvStructure is modified.
It can do this on a field by field basis.</dd>
</dl>
</dd>
</dl>
<p>The following sections describes the classes required for the first phase.</p>
<h2>database</h2>
<p>This directory has the following files:</p>
<dl>
<dt>pvDatabase.h</dt>
<dd>
This is what is described in this section.
</dd>
<dt>pvDatabase.cpp</dt>
<dd>
The implementation of PVDatabase.
</dd>
<dt>pvRecord.cpp</dt>
<dd>
The implementation of the base class for PVREcord.
It can also implement record instances with a process
method does nothing.
This can be used to create a "dumb" record where all changes are
done by clients.
The complete implementation is provided in the header file.
Thus code will be generated only if other code includes the
header file and creates a record instance.
</dd>
<dt>exampleCounter.h</dt>
<dd>
This was described in the introduction.
</dd>
<dt>powerSupplyRecordTest.h</dt>
<dd>
This provides code that simulates a power supply.
It computes the current from the voltage and power.
It is used for testing.
The complete implementation is provided in the header file.
Thus code will be generated only if other code includes the
header file and creates a record instance.
</dd>
<dt>recordList.h</dt>
<dd>This implements a PVRecord that provides a list of the names
of the records in the PVDatabase.
It also serves as an example of how to implement a service.
The testExampleServer creates an instance via the following code:
<pre>
recordName = "laptoprecordListPGRPC";
pvRecord = RecordListRecord::create(recordName);
result = master-&gt;addRecord(pvRecord);
</pre>
</dd>
</dl>
<p>The classes in pvDatabase.h describe a database of memory resident
smart records.
It describes the following classes:</p>
<dl>
<dt>PVRecord</dt>
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
<dt>PVRecordField</dt>
<dt>PVRecordStructure</dt>
<dd>These wrap PVField and PVStructure so that pvCopy and monitor
can be implemented.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>PVListener</dt>
<dd>This is implemented by anything that wants to trap calls to PVRecord::message.</dd>
<dt>PVDatabase</dt>
<dd>This is a database of PVRecords.</dd>
</dl>
<p>Each class is described in a separate subsection.</p>
<h3>C++ namespace and typedefs</h3>
<pre>
namespace epics { namespace pvDatabase {
class PVRecord;
typedef std::tr1::shared_ptr&lt;PVRecord&gt; PVRecordPtr;
typedef std::map&lt;epics::pvData::String,PVRecordPtr&gt; PVRecordMap;
class PVRecordField;
typedef std::tr1::shared_ptr&lt;PVRecordField&gt; PVRecordFieldPtr;
typedef std::vector&lt;PVRecordFieldPtr&gt; PVRecordFieldPtrArray;
typedef std::tr1::shared_ptr&lt;PVRecordFieldPtrArray&gt; PVRecordFieldPtrArrayPtr;
class PVRecordStructure;
typedef std::tr1::shared_ptr&lt;PVRecordStructure&gt; PVRecordStructurePtr;
class PVRecordClient;
typedef std::tr1::shared_ptr&lt;PVRecordClient&gt; PVRecordClientPtr;
class PVListener;
typedef std::tr1::shared_ptr&lt;PVListener&gt; PVListenerPtr;
class RecordProcessRequester;
typedef std::tr1::shared_ptr&lt;RecordProcessRequester&gt; RecordProcessRequesterPtr;
class RecordPutRequester;
typedef std::tr1::shared_ptr&lt;RecordPutRequester&gt; RecordPutRequesterPtr;
class PVDatabase;
typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
</pre>
<h3>class PVRecord</h3>
<p>NOTES:
<ul>
<li>This section uses the name record instead of "an instance of PVRecord".</li>
<li>Most clients will access a record via the local channel provider,
i. e. via pvAccess.
Thus this section is mainly of interest to
the local channel provider and record implementers.</li>
<li>Most readers will not care about most of the PVRecord methods.
Most of the methods are used by the pvAccess code.
Service implementers will mostly be interested in methods init and process.
These are described first.
</ul>
<hr>PVRecord Methods</h4>
<pre>
class PVRecord
public epics::pvData::Requester,
public std::tr1::enable_shared_from_this&lt;PVRecord&gt;
{
public:
POINTER_DEFINITIONS(PVRecord);
virtual bool init() {initPVRecord(); return true;}
virtual void process() {}
static PVRecordPtr create(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void destroy();
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField);
bool addRequester(epics::pvData::RequesterPtr const &amp; requester);
bool removeRequester(epics::pvData::RequesterPtr const &amp; requester);
inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
void lock();
void unlock();
bool tryLock();
void lockOtherRecord(PVRecordPtr const &amp; otherRecord);
bool addPVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
bool removePVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
bool addListener(PVListenerPtr const &amp; pvListener);
bool removeListener(PVListenerPtr const &amp; pvListener);
void beginGroupPut();
void endGroupPut();
epics::pvData::String getRequesterName() {return getRecordName();}
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void message(
PVRecordFieldPtr const &amp; pvRecordField,
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void toString(epics::pvData::StringBuilder buf);
void toString(epics::pvData::StringBuilder buf,int indentLevel);
protected:
PVRecord(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
void initPVRecord();
epics::pvData::PVStructurePtr getPVStructure();
PVRecordPtr getPtrSelf()
{
return shared_from_this();
}
private:
...
}
</pre>
<p>The methods are:</h3>
<dl>
<dt>init</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
This method Must call initPVRecord.</p>
</dd>
<dt>process</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
The base implementation does nothing.</p>
</dd>
<dt>create</dt>
<dd>Static method to create dumb records, i.e. records with a process method
that does nothing.</dd>
<dt>~PVRecord</dt>
<dd>The destructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
<dt>destroy</dt>
<dd>This is a virtual method.
</dd>
<dt>getRecordName</dt>
<dd>Return the recordName.</dd>
<dt>getPVRecordStructure</dt>
<dd>Get the top level PVStructure.</dd>
<dt>findPVRecordField</dt>
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>lock_guard</dt>
<dd>This is an inline method that locks the record. The record will automatically
be unlocked when control leaves the block that has the call.
<dt>lock</dt>
<dt>unlock</dt>
<dd>Lock and Unlock the record.
Any code accessing the data in the record or calling other PVRecord methods
must have the record locked.</dd>
<dt>tryLock</dt>
<dd>If true then just like lock.
If falseclient can not access record.
A client can try to simultaneously hold the lock for more than two records
by calling this method. But must be willing to accept failure.
</dd>
<dt>lockOtherRecord</dt>
<dd>A client that holds the lock for one record can lock one other record.
A client must not call this if the client already has the lock for
more then one record.
</dd>
<dt>addPVRecordClient</dt>
<dd>Every client that accesses the record must call this so that the client can be notified when the record is deleted.</dd>
<dt>removePVRecordClient</dt>
<dd>Client is no longer accessing the record.</dd>
<dt>detachClients</dt>
<dd>Ask all clients to detach from the record</dd>
<dt>addListener</dt>
<dd>Add a PVListener. This must be called before calling pvRecordField.addListener.</dd>
<dt>removeListener</dt>
<dd>Removes a listener. The listener will also be removed from all fields to which it is attached.</dd>
<dt>beginGroupPut</dt>
<dd>Begin a group of puts.
This results in all registered PVListeners being called</dd>
<dt>endGroupPut</dt>
<dd>End a group of puts.
This results in all registered PVListeners being called.</dd>
<dt>getRequesterName</dt>
<dd>virtual method of Requester
</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
<dt>toString</dt>
<dd>Just calls the top level PVStructure toString method.</dd>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>initPVRecord</dt>
<dd>This method must be called by derived class.</dd>
<dt>getPVStructure</dt>
<dd>Called by derived class.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
class PVRecordField {
public virtual epics::pvData::PostHandler,
public std::tr1::enable_shared_from_this&lt;PVRecordField&gt;
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField,
PVRecordStructurePtr const &amp;parent,
PVRecordPtr const &amp; pvRecord);
virtual ~PVRecordField();
virtual void destroy();
PVRecordStructurePtr getParent();
epics::pvData::PVFieldPtr getPVField();
epics::pvData::String getFullFieldName();
epics::pvData::String getFullName();
PVRecordPtr getPVRecord();
bool addListener(PVListenerPtr const &amp; pvListener);
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
protected:
PVRecordFieldPtr getPtrSelf()
{
return shared_from_this();
}
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordField</dt>
<dd>The constructor.</dd>
<dt>~PVRecordField</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>Called by PVRecordStructure when it's destroy method is called.</dd>
<dt>getParent</dt>
<dd>Get the parent PVRecordStructure for this field.</dd>
<dt>getPVField</dt>
<dd>Get the PVField associated with this PVRecordField.</dd>
<dt>getFullFieldName</dt>
<dd>This gets the full name of the field, i.e. field,field,..</dd>
<dt>getFullName</dt>
<dd>This gets recordName plus the full name of the field, i.e. recordName.field,field,..</dd>
<dt>getPVRecord</dt>
<dd>Returns the PVRecord to which this field belongs.</dd>
<dt>addListener</dt>
<dd>Add A PVListener to this field.
Whenever this field or any subfield if this field is modified the listener will be notified.
PVListener is described below.
Before a listener can call addListener it must first call PVRecord.registerListener.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVRecordStructure</h3>
<pre>
class PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
PVRecordStructure(
epics::pvData::PVStructurePtr const &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField);
virtual ~PVRecordStructure();
virtual void destroy();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
protected:
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordStructure</dt>
<dd>The constructor.</dd>
<dt>~PVRecordStructure</dt>
<dd>The destructor.</dd>
<dt>getPVRecordFields</dt>
<dd>Get the PVRecordField array for the subfields</dd>
<dt>getPVStructure</dt>
<dd>Get the PVStructure for this field.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<h3>class PVRecordClient</h3>
<pre>
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const &amp; pvRecord);
};
</pre>
<p>where</p>
<dl>
<dt>~PVRecordClient</dt>
<dd>The destructor.</dd>
<dt>detach</dt>
<dd>The record is being removed from the master database,</dd>
</dl>
</dl>
<h3>class PVListener</h3>
<pre>
class PVListener {
virtual public PVRecordClient
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener();
virtual void dataPut(PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~PVListener</dt>
<dd>The destructor.</dd>
<dt>dataPut(PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
This is called if the listener has called PVRecordField::addListener for pvRecordField.</dd>
<dt>dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
Requested is the field to which the requester issued a pvField-&amp;addListener.
This is called if the listener has called PVRecordField-&amp;addListener for requested.</dd>
<dt>beginGroupPut</dt>
<dd>A related set of changes is being started.</dd>
<dt>endGroupPut</dt>
<dd>A related set of changes is done.</dd>
</dl>
<h3>class PVDatabase</h3>
<pre>
class PVDatabase : virtual public epics::pvData::Requester {
public:
POINTER_DEFINITIONS(PVDatabase);
static PVDatabasePtr getMaster();
virtual ~PVDatabase();
virtual void destroy();
PVRecordPtr findRecord(epics::pvData::String const&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
virtual epics::pvData::String getRequesterName();
virtual void message(
epics::pvData::String const &amp;message,
epics::pvData::MessageType messageType);
private:
PVDatabase();
};
</pre>
<p>where</p>
<dl>
<dt>getMaster</dt>
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
<dt>~PVDatabase</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>This is called by remote channelAccess when process exits.
This destroys and removes all records in the PVDatabase.</dd>
<dt>findRecord</dt>
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
<dt>addRecord</dt>
<dd>Add a record to the database.
If the record already exists it is not modified and false is returned.</dd>
<dt>removeRecord</dt>
<dd>Remove a record from the database.
If the record was not in the database false is returned.</dd>
<dt>getRequesterName</dt>
<dd>Virtual method of Requester</dd>
<dt>message</dt>
<dd>Virtual message of Requester.</dd>
</dl>
<h2>pvAccess</h2>
<p>This is code that provides an implementation of channelProvider as
defined by pvAccess.
It provides access to PVRecords and is access by the server side of remote pvAccess.</p>
<h3>channelProviderLocal</h3>
<p>This is a complete implementation of channelProvider and ,
except for channelRPC, provides a complete implementation of Channel
as defined by pvAccess.
For monitors it calls the code described in the following sections.</p>
<h3>ChannelLocalDebug</h3>
<p>The channelProvider implementation provides the ability to generate
debug messages based a debug level with the following meaning:</p>
<dl>
<dt>&lt;=0</dt>
<dd>No debug messages </dd>
<dt>&gt;0</dt>
<dd>Generate a message when anything is created or destroyed</dd>
<dt>&gt;1</dt>
<dd>Also generate processing messages.</dd>
</dl>
<p>ChannelProviderLocal has a method:</p>
<pre>
void createChannelLocalDebugRecord(
String const &amp; recordName);
</pre>
<p>This method creates a PVRecord that allows a pvAccess client to set the
debug level.</p>
<h3>pvCopy</h3>
<p>This provides code that creates a top level PVStructure that is an arbitrary
subset of the fields in the PVStructure from a PVRecord.
In addition it provides code that monitors changes to the fields in a PVRecord.
A client configures the desired set of subfields and monitoring options
via a pvRequest structure.
pvAccess provides a class CreatePVRequest that creates a pvRequest.
The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava.
</p>
<h3>monitorAlgorithm</h3>
<p>Currently all that is implemented is a header file.
The only algorithm currently implemented is <b>onPut</b>
</p>
<h3>monitorFactory</h3>
<h4>Overview</h4>
<p><b>epics::pvData::monitor</b> defines the monitor interfaces
as seen by a client.
See
<a href="http://epics-pvdata.sourceforge.net/docbuild/pvDatabaseCPP/tip/documentation/pvDatabaseCPP.html">pvDatabaseCPP.html</a>
For details.</p>
<p>
monitorFactory implements the
monitoring interfaces for a PVRecord.
It implements queueSize=0 and queueSize&gt;=2.
</p>
<p>
The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy.
When PVCopyMonitor tells monitor that changes
have occurred, monitor applies the appropriate algorithm to each changed field.</p>
<p>Currently only algorithm <b>onPut</b> is implemented but,
like pvIOCJava there are plans to support for the following monitor algorithms:</p>
<dl>
<dt>onPut</dt>
<dd>A monitor is issued whenever a put is issued to the field. This is the
default unless the record defines deadbands for a field. An exception is
the top level timeStamp which by default is made onChange and monitor
will not be raised.</dd>
<dt>onChange</dt>
<dd>This provides two options: 1) A monitor is raised whenever a field
changes value, and 2) A monitor will never be raised for the field.</dd>
<dt>deadband</dt>
<dd>The field must be a numeric scalar. Whenever the absolute or percentage
value of the field changes by more than a deadband a monitor is issued.
The record instance can also define deadbands.</dd>
<dt>periodic</dt>
<dd>A monitor is issued at a periodic rate if a put was issued to any field
being monitored.</dd>
</dl>
<h4>MonitorFactory</h4>
<p>MonitorFactory provides the following methods:</p>
<pre>class MonitorFactory
{
static MonitorPtr create(
PVRecordPtr const &amp; pvRecord,
MonitorRequester::shared_pointer const &amp; monitorRequester,
PVStructurePtr const &amp; pvRequest);
static void registerMonitorAlgorithmCreater(
MonitorAlgorithmCreatePtr const &amp; monitorAlgorithmCreate,
String const &amp; algorithmName);
}</pre>
<p>where</p>
<dl>
<dt>create</dt>
<dd>Create a monitor. The arguments are:
<dl>
<dt>pvRecord</dt>
<dd>The record being monitored.</dd>
<dt>monitorRequester</dt>
<dd>The monitor requester. This is the code to which monitot events
will be delivered.</dd>
<dt>pvRequest</dt>
<dd>The request options</dd>
</dl>
</dd>
<dt>registerMonitorAlgorithmCreater</dt>
<dd>Called by code that implements a monitor algorithm.</dd>
</dl>
<h3>channelLocalDebugRecord</h3>
<p>This implements a PVRecord that allows a client to set
a debug level for the local channel provider implementation.
The top level structure has a single integer field named value.
See ChannelProviderLocal for the meaning associated with value.</p>
<p>ChannelProviderLocal has a method:</p>
<pre>
void createChannelLocalDebugRecord(String const &amp; recordName);
</pre>
<p>This creates an instance of a ChannelLocalDebugRecord and installs it
into the PVDatabase.</p>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +0,0 @@
TOP = ..
include $(TOP)/configure/CONFIG
DIRS += src
DIRS += record
DIRS += pvCopy
DIRS += exampleCounter
DIRS += exampleServer
DIRS += examplePVADoubleArrayGet
DIRS += arrayPerformance
DIRS += v3IOC
include $(TOP)/configure/RULES_DIRS

View File

@@ -1,20 +0,0 @@
TOP=../..
include $(TOP)/configure/CONFIG
PROD_HOST += arrayPerformanceMain
arrayPerformanceMain_SRCS += arrayPerformanceMain.cpp
arrayPerformanceMain_LIBS += pvDatabase pvAccess pvData Com
arrayPerformanceMain_LIBS += pvDatabaseExample
PROD_HOST += longArrayMonitorMain
longArrayMonitorMain_SRCS += longArrayMonitorMain.cpp
longArrayMonitorMain_LIBS += pvDatabase pvAccess pvData Com
longArrayMonitorMain_LIBS += pvDatabaseExample
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,108 +0,0 @@
/*arrayPerformanceMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.08
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <vector>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/arrayPerformance.h>
#include <pv/longArrayMonitor.h>
#include <pv/traceRecord.h>
#include <pv/channelProviderLocal.h>
#include <pv/serverContext.h>
#include <pv/clientFactory.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
int main(int argc,char *argv[])
{
bool result(false);
String recordName;
recordName = "arrayPerformance";
size_t size = 50000000;
double delay = .01;
String providerName("local");
size_t nMonitor = 1;
bool useQueue = false;
if(argc==2 && String(argv[1])==String("-help")) {
cout << "arrayPerformanceMain recordName size";
cout << " delay providerName nMonitor useQueue" << endl;
cout << "default" << endl;
cout << "arrayPerformance ";
cout << recordName << " ";
cout << size << " ";
cout << delay << " ";
cout << providerName << " ";
cout << nMonitor << " ";
cout << (useQueue ? "true" : "false") << endl;
return 0;
}
if(argc>1) recordName = argv[1];
if(argc>2) size = strtoul(argv[2],0,0);
if(argc>3) delay = atof(argv[3]);
if(argc>4) providerName = argv[4];
if(argc>5) nMonitor = strtoul(argv[5],0,0);
if(argc>6) useQueue = (argv[6]==String("true") ? true : false);
cout << "arrayPerformance ";
cout << recordName << " ";
cout << size << " ";
cout << delay << " ";
cout << providerName << " ";
cout << nMonitor << " ";
cout << (useQueue ? "true" : "false") << endl;
ClientFactory::start();
PVDatabasePtr master = PVDatabase::getMaster();
ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
PVRecordPtr pvRecord;
pvRecord = ArrayPerformance::create(recordName,size,delay);
result = master->addRecord(pvRecord);
pvRecord = TraceRecord::create("traceRecordPGRPC");
result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl;
pvRecord.reset();
ServerContext::shared_pointer pvaServer =
startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
std::vector<LongArrayMonitorPtr> longArrayMonitor(nMonitor);
for(size_t i=0; i<nMonitor; ++i) {
longArrayMonitor[i]
= LongArrayMonitor::create(providerName,recordName,useQueue);
}
for(size_t i=0; i<nMonitor; ++i) longArrayMonitor[i]->start();
cout << "arrayPerformance\n";
string str;
while(true) {
cout << "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
for(size_t i=0; i<nMonitor; ++i) longArrayMonitor[i]->stop();
pvaServer->shutdown();
epicsThreadSleep(1.0);
pvaServer->destroy();
ClientFactory::stop();
channelProvider->destroy();
return 0;
}

View File

@@ -1,70 +0,0 @@
/*longArrayMonitorMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.10
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <vector>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/arrayPerformance.h>
#include <pv/longArrayMonitor.h>
#include <pv/traceRecord.h>
#include <pv/channelProviderLocal.h>
#include <pv/serverContext.h>
#include <pv/clientFactory.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
int main(int argc,char *argv[])
{
String channelName("arrayPerformance");
bool useQueue = false;
if(argc==2 && String(argv[1])==String("-help")) {
cout << "longArrayMonitorMain channelName useQueue" << endl;
cout << "default" << endl;
cout << "longArrayMonitorMain " << channelName << " ";
cout << (useQueue ? "true" : "false") << endl;
return 0;
}
ClientFactory::start();
if(argc>1) channelName = argv[1];
if(argc>2) useQueue = (String(argv[2])==String("true") ? true : false);
cout << "longArrayMonitorMain " << channelName << " ";
cout << (useQueue ? "true" : "false") << endl;
LongArrayMonitorPtr longArrayMonitor
= LongArrayMonitor::create("pvAccess",channelName,useQueue);
longArrayMonitor->start();
cout << "longArrayMonitor\n";
string str;
while(true) {
cout << "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
longArrayMonitor->destroy();
longArrayMonitor.reset();
ClientFactory::stop();
return 0;
}

View File

@@ -1,13 +0,0 @@
TOP=../..
include $(TOP)/configure/CONFIG
PROD_HOST += exampleCounterMain
exampleCounterMain_SRCS += exampleCounterMain.cpp
exampleCounterMain_LIBS += pvDatabase pvAccess pvData Com
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,66 +0,0 @@
/*ExampleCounterMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/exampleCounter.h>
#include <pv/traceRecord.h>
#include <pv/channelProviderLocal.h>
#include <pv/serverContext.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
int main(int argc,char *argv[])
{
PVDatabasePtr master = PVDatabase::getMaster();
ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
PVRecordPtr pvRecord;
bool result(false);
String recordName;
recordName = "exampleCounter";
pvRecord = ExampleCounter::create(recordName);
result = master->addRecord(pvRecord);
cout << "result of addRecord " << recordName << " " << result << endl;
recordName = "traceRecordPGRPC";
pvRecord = TraceRecord::create(recordName);
result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl;
pvRecord.reset();
ServerContext::shared_pointer pvaServer =
startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
cout << "exampleCounter\n";
string str;
while(true) {
cout << "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
pvaServer->shutdown();
epicsThreadSleep(1.0);
pvaServer->destroy();
channelProvider->destroy();
return 0;
}

View File

@@ -1,14 +0,0 @@
TOP=../..
include $(TOP)/configure/CONFIG
PROD_HOST += examplePVADoubleArrayGetMain
examplePVADoubleArrayGetMain_SRCS += examplePVADoubleArrayGetMain.cpp
examplePVADoubleArrayGetMain_LIBS += pvDatabase pvAccess pvData Com
examplePVADoubleArrayGetMain_LIBS += pvDatabaseExample
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,80 +0,0 @@
/*ExamplePVADoubleArrayGetMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/examplePVADoubleArrayGet.h>
#include <pv/traceRecord.h>
#include <pv/channelProviderLocal.h>
#include <pv/serverContext.h>
#include <pv/clientFactory.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
static StandardPVFieldPtr standardPVField = getStandardPVField();
int main(int argc,char *argv[])
{
PVDatabasePtr master = PVDatabase::getMaster();
ClientFactory::start();
ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
PVRecordPtr pvRecord;
bool result(false);
String recordName;
PVStructurePtr pvStructure = standardPVField->scalarArray(
pvDouble,"alarm,timeStamp");
recordName = "doubleArray";
pvRecord = PVRecord::create(recordName,pvStructure);
result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl;
ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
recordName = "examplePVADoubleArrayGet";
if(argc>1) recordName = argv[1];
String providerName("local");
if(argc>2) providerName = argv[2];
String channelName("doubleArray");
if(argc>3) channelName = argv[3];
pvRecord = ExamplePVADoubleArrayGet::create(
recordName,providerName,channelName);
if(pvRecord!=NULL) {
result = master->addRecord(pvRecord);
cout << "result of addRecord " << recordName << " " << result << endl;
} else {
cout << "ExamplePVADoubleArrayGet::create failed" << endl;
}
string str;
while(true) {
cout << "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
serverContext->shutdown();
epicsThreadSleep(1.0);
serverContext->destroy();
ClientFactory::stop();
channelProvider->destroy();
return 0;
}

View File

@@ -1,12 +0,0 @@
TOP=../..
include $(TOP)/configure/CONFIG
PROD_HOST += exampleServerMain
exampleServerMain_SRCS += exampleServerMain.cpp
exampleServerMain_LIBS += pvDatabase pvDatabaseExample pvAccess pvData Com
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,57 +0,0 @@
/*exampleServerMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <vector>
#include <iostream>
#include <pv/channelProviderLocal.h>
#include <pv/exampleServerCreateRecords.h>
#include <pv/serverContext.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
int main(int argc,char *argv[])
{
PVDatabasePtr master = PVDatabase::getMaster();
ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
ExampleServerCreateRecords::create();
ServerContext::shared_pointer ctx =
startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
cout << "exampleServer\n";
PVStringArrayPtr pvNames = master->getRecordNames();
String buffer;
pvNames->toString(&buffer);
cout << "recordNames" << endl << buffer << endl;
string str;
while(true) {
cout << "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
ctx->destroy();
epicsThreadSleep(1.0);
channelProvider->destroy();
return 0;
}

View File

@@ -1,16 +0,0 @@
TOP=../..
include $(TOP)/configure/CONFIG
PROD_HOST += testPVCopy
testPVCopy_SRCS += testPVCopy.cpp
testPVCopy_LIBS += pvDatabase pvAccess pvData Com
PROD_HOST += testPVRecord
testPVRecord_SRCS += testPVRecord.cpp
testPVRecord_LIBS += pvDatabase pvAccess pvData Com
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,423 +0,0 @@
/*testPVCopyMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <epicsStdio.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/channelProviderLocal.h>
#include <pv/powerSupplyRecordTest.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
class MyRequester;
typedef std::tr1::shared_ptr<MyRequester> MyRequesterPtr;
class MyRequester : public Requester {
public:
POINTER_DEFINITIONS(MyRequester);
MyRequester(String const &requesterName)
: requesterName(requesterName)
{}
virtual ~MyRequester() {}
virtual String getRequesterName() { return requesterName;}
virtual void message(String const & message,MessageType messageType)
{
cout << message << endl;
}
private:
String requesterName;
};
static PVRecordPtr createScalar(
String const & recordName,
ScalarType scalarType,
String const & properties)
{
PVStructurePtr pvStructure = getStandardPVField()->scalar(scalarType,properties);
return PVRecord::create(recordName,pvStructure);
}
static PVRecordPtr createScalarArray(
String const & recordName,
ScalarType scalarType,
String const & properties)
{
PVStructurePtr pvStructure = getStandardPVField()->scalarArray(scalarType,properties);
return PVRecord::create(recordName,pvStructure);
}
static PowerSupplyRecordTestPtr createPowerSupply(String const & recordName)
{
FieldCreatePtr fieldCreate = getFieldCreate();
StandardFieldPtr standardField = getStandardField();
PVDataCreatePtr pvDataCreate = getPVDataCreate();
size_t nfields = 5;
StringArray names;
names.reserve(nfields);
FieldConstPtrArray powerSupply;
powerSupply.reserve(nfields);
names.push_back("alarm");
powerSupply.push_back(standardField->alarm());
names.push_back("timeStamp");
powerSupply.push_back(standardField->timeStamp());
String properties("alarm,display");
names.push_back("voltage");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
names.push_back("power");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
names.push_back("current");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
return PowerSupplyRecordTest::create(recordName,
pvDataCreate->createPVStructure(
fieldCreate->createStructure(names,powerSupply)));
}
static void testPVScalar(
String const & valueNameRecord,
String const & valueNameCopy,
PVRecordPtr const & pvRecord,
PVCopyPtr const & pvCopy)
{
PVRecordFieldPtr pvRecordField;
PVStructurePtr pvStructureRecord;
PVStructurePtr pvStructureCopy;
PVFieldPtr pvField;
PVScalarPtr pvValueRecord;
PVScalarPtr pvValueCopy;
BitSetPtr bitSet;
String builder;
size_t offset;
ConvertPtr convert = getConvert();
cout << endl;
pvStructureRecord = pvRecord->getPVRecordStructure()->getPVStructure();
pvField = pvStructureRecord->getSubField(valueNameRecord);
pvValueRecord = static_pointer_cast<PVScalar>(pvField);
convert->fromDouble(pvValueRecord,.04);
StructureConstPtr structure = pvCopy->getStructure();
builder.clear(); structure->toString(&builder);
cout << "structure from copy" << endl << builder << endl;
pvStructureCopy = pvCopy->createPVStructure();
pvField = pvStructureCopy->getSubField(valueNameCopy);
pvValueCopy = static_pointer_cast<PVScalar>(pvField);
bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
pvCopy->initCopy(pvStructureCopy, bitSet);
cout << "after initCopy pvValueCopy " << convert->toDouble(pvValueCopy);
cout << endl;
convert->fromDouble(pvValueRecord,.06);
pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
cout << "after put(.06) pvValueCopy " << convert->toDouble(pvValueCopy);
builder.clear();
bitSet->toString(&builder);
cout << " bitSet " << builder;
cout << endl;
pvRecordField = pvRecord->findPVRecordField(pvValueRecord);
offset = pvCopy->getCopyOffset(pvRecordField);
cout << "getCopyOffset() " << offset;
cout << " pvValueCopy->getOffset() " << pvValueCopy->getFieldOffset();
cout << " pvValueRecord->getOffset() " << pvValueRecord->getFieldOffset();
cout << " bitSet " << builder;
cout << endl;
bitSet->clear();
convert->fromDouble(pvValueRecord,1.0);
builder.clear();
bitSet->toString(&builder);
cout << "before updateCopyFromBitSet";
cout << " recordValue " << convert->toDouble(pvValueRecord);
cout << " copyValue " << convert->toDouble(pvValueCopy);
cout << " bitSet " << builder;
cout << endl;
bitSet->set(0);
pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet);
cout << "after updateCopyFromBitSet";
cout << " recordValue " << convert->toDouble(pvValueRecord);
cout << " copyValue " << convert->toDouble(pvValueCopy);
cout << " bitSet " << builder;
cout << endl;
convert->fromDouble(pvValueCopy,2.0);
bitSet->set(0);
cout << "before updateRecord";
cout << " recordValue " << convert->toDouble(pvValueRecord);
cout << " copyValue " << convert->toDouble(pvValueCopy);
cout << " bitSet " << builder;
cout << endl;
pvCopy->updateRecord(pvStructureCopy,bitSet);
cout << "after updateRecord";
cout << " recordValue " << convert->toDouble(pvValueRecord);
cout << " copyValue " << convert->toDouble(pvValueCopy);
cout << " bitSet " << builder;
cout << endl;
}
static void testPVScalarArray(
ScalarType scalarType,
String const & valueNameRecord,
String const & valueNameCopy,
PVRecordPtr const & pvRecord,
PVCopyPtr const & pvCopy)
{
PVRecordFieldPtr pvRecordField;
PVStructurePtr pvStructureRecord;
PVStructurePtr pvStructureCopy;
PVScalarArrayPtr pvValueRecord;
PVScalarArrayPtr pvValueCopy;
BitSetPtr bitSet;
String builder;
size_t offset;
size_t n = 5;
shared_vector<double> values(n);
cout << endl;
pvStructureRecord = pvRecord->getPVRecordStructure()->getPVStructure();
pvValueRecord = pvStructureRecord->getScalarArrayField(valueNameRecord,scalarType);
for(size_t i=0; i<n; i++) values[i] = i;
const shared_vector<const double> xxx(freeze(values));
pvValueRecord->putFrom(xxx);
StructureConstPtr structure = pvCopy->getStructure();
builder.clear(); structure->toString(&builder);
cout << "structure from copy" << endl << builder << endl;
pvStructureCopy = pvCopy->createPVStructure();
pvValueCopy = pvStructureCopy->getScalarArrayField(valueNameCopy,scalarType);
bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
pvCopy->initCopy(pvStructureCopy, bitSet);
builder.clear(); pvValueCopy->toString(&builder);
cout << "after initCopy pvValueCopy " << builder << endl;
cout << endl;
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + .06;
const shared_vector<const double> yyy(freeze(values));
pvValueRecord->putFrom(yyy);
pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
builder.clear(); pvValueCopy->toString(&builder);
cout << "after put(i+ .06) pvValueCopy " << builder << endl;
builder.clear();
bitSet->toString(&builder);
cout << " bitSet " << builder;
cout << endl;
pvRecordField = pvRecord->findPVRecordField(pvValueRecord);
offset = pvCopy->getCopyOffset(pvRecordField);
cout << "getCopyOffset() " << offset;
cout << " pvValueCopy->getOffset() " << pvValueCopy->getFieldOffset();
cout << " pvValueRecord->getOffset() " << pvValueRecord->getFieldOffset();
builder.clear();
bitSet->toString(&builder);
cout << " bitSet " << builder;
cout << endl;
bitSet->clear();
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + 1.0;
const shared_vector<const double> zzz(freeze(values));
pvValueRecord->putFrom(zzz);
builder.clear();
bitSet->toString(&builder);
cout << "before updateCopyFromBitSet";
builder.clear(); pvValueRecord->toString(&builder);
cout << " recordValue " << builder << endl;
builder.clear(); pvValueCopy->toString(&builder);
cout << " copyValue " << builder << endl;
cout << " bitSet " << builder;
builder.clear();
bitSet->toString(&builder);
cout << endl;
bitSet->set(0);
pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet);
cout << "after updateCopyFromBitSet";
builder.clear(); pvValueRecord->toString(&builder);
cout << " recordValue " << builder << endl;
builder.clear(); pvValueCopy->toString(&builder);
cout << " copyValue " << builder << endl;
builder.clear();
bitSet->toString(&builder);
cout << " bitSet " << builder;
cout << endl;
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + 2.0;
const shared_vector<const double> ttt(freeze(values));
pvValueRecord->putFrom(ttt);
bitSet->set(0);
cout << "before updateRecord";
builder.clear(); pvValueRecord->toString(&builder);
cout << " recordValue " << builder << endl;
builder.clear(); pvValueCopy->toString(&builder);
cout << " copyValue " << builder << endl;
builder.clear();
bitSet->toString(&builder);
cout << " bitSet " << builder;
cout << endl;
pvCopy->updateRecord(pvStructureCopy,bitSet);
cout << "after updateRecord";
builder.clear(); pvValueRecord->toString(&builder);
cout << " recordValue " << builder << endl;
builder.clear(); pvValueCopy->toString(&builder);
cout << " copyValue " << builder << endl;
builder.clear();
bitSet->toString(&builder);
cout << " bitSet " << builder;
cout << endl;
}
static void scalarTest()
{
cout << endl << endl << "****scalarTest****" << endl;
RequesterPtr requester(new MyRequester("exampleTest"));
PVRecordPtr pvRecord;
String request;
PVStructurePtr pvRequest;
PVRecordFieldPtr pvRecordField;
PVCopyPtr pvCopy;
String builder;
String valueNameRecord;
String valueNameCopy;
pvRecord = createScalar("doubleRecord",pvDouble,"alarm,timeStamp,display");
valueNameRecord = request = "value";
pvRequest = getCreateRequest()->createRequest(request,requester);
builder.clear(); pvRequest->toString(&builder);
cout << "request " << request << endl;
cout << "pvRequest" << endl << builder;
pvCopy = PVCopy::create(pvRecord,pvRequest,"");
valueNameCopy = "value";
testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy);
request = "";
valueNameRecord = "value";
pvRequest = getCreateRequest()->createRequest(request,requester);
builder.clear(); pvRequest->toString(&builder);
cout << "request " << request << endl << "pvRequest" << endl << builder << endl;
pvCopy = PVCopy::create(pvRecord,pvRequest,"");
valueNameCopy = "value";
testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy);
request = "alarm,timeStamp,value";
valueNameRecord = "value";
pvRequest = getCreateRequest()->createRequest(request,requester);
builder.clear(); pvRequest->toString(&builder);
cout << "request " << request << endl << "pvRequest" << endl << builder << endl;
pvCopy = PVCopy::create(pvRecord,pvRequest,"");
valueNameCopy = "value";
testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy);
pvRecord->destroy();
}
static void arrayTest()
{
cout << endl << endl << "****arrayTest****" << endl;
RequesterPtr requester(new MyRequester("exampleTest"));
PVRecordPtr pvRecord;
String request;
PVStructurePtr pvRequest;
PVRecordFieldPtr pvRecordField;
PVCopyPtr pvCopy;
String builder;
String valueNameRecord;
String valueNameCopy;
pvRecord = createScalarArray("doubleArrayRecord",pvDouble,"alarm,timeStamp");
valueNameRecord = request = "value";
pvRequest = getCreateRequest()->createRequest(request,requester);
builder.clear(); pvRequest->toString(&builder);
cout << "request " << request << endl;
cout << "pvRequest" << endl << builder;
pvCopy = PVCopy::create(pvRecord,pvRequest,"");
valueNameCopy = "value";
testPVScalarArray(pvDouble,valueNameRecord,valueNameCopy,pvRecord,pvCopy);
request = "";
valueNameRecord = "value";
pvRequest = getCreateRequest()->createRequest(request,requester);
builder.clear(); pvRequest->toString(&builder);
cout << "request " << request << endl << "pvRequest" << endl << builder << endl;
pvCopy = PVCopy::create(pvRecord,pvRequest,"");
valueNameCopy = "value";
testPVScalarArray(pvDouble,valueNameRecord,valueNameCopy,pvRecord,pvCopy);
request = "alarm,timeStamp,value";
valueNameRecord = "value";
pvRequest = getCreateRequest()->createRequest(request,requester);
builder.clear(); pvRequest->toString(&builder);
cout << "request " << request << endl << "pvRequest" << endl << builder << endl;
pvCopy = PVCopy::create(pvRecord,pvRequest,"");
valueNameCopy = "value";
testPVScalarArray(pvDouble,valueNameRecord,valueNameCopy,pvRecord,pvCopy);
pvRecord->destroy();
}
static void powerSupplyTest()
{
cout << endl << endl << "****powerSupplyTest****" << endl;
RequesterPtr requester(new MyRequester("exampleTest"));
PowerSupplyRecordTestPtr pvRecord;
String request;
PVStructurePtr pvRequest;
PVRecordFieldPtr pvRecordField;
PVCopyPtr pvCopy;
String builder;
String valueNameRecord;
String valueNameCopy;
pvRecord = createPowerSupply("powerSupply");
valueNameRecord = request = "power.value";
pvRequest = getCreateRequest()->createRequest(request,requester);
builder.clear(); pvRequest->toString(&builder);
cout << "request " << request << endl;
cout << "pvRequest" << endl << builder;
pvCopy = PVCopy::create(pvRecord,pvRequest,"");
valueNameCopy = "value";
testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy);
request = "";
valueNameRecord = "power.value";
pvRequest = getCreateRequest()->createRequest(request,requester);
builder.clear(); pvRequest->toString(&builder);
cout << "request " << request << endl << "pvRequest" << endl << builder << endl;
pvCopy = PVCopy::create(pvRecord,pvRequest,"");
valueNameCopy = "power.value";
testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy);
request = "alarm,timeStamp,voltage.value,power.value,current.value";
valueNameRecord = "power.value";
pvRequest = getCreateRequest()->createRequest(request,requester);
builder.clear(); pvRequest->toString(&builder);
cout << "request " << request << endl << "pvRequest" << endl << builder << endl;
pvCopy = PVCopy::create(pvRecord,pvRequest,"");
valueNameCopy = "power";
testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy);
request = "alarm,timeStamp,voltage{value,alarm},power{value,alarm,display},current.value";
valueNameRecord = "power.value";
pvRequest = getCreateRequest()->createRequest(request,requester);
builder.clear(); pvRequest->toString(&builder);
cout << "request " << request << endl << "pvRequest" << endl << builder << endl;
pvCopy = PVCopy::create(pvRecord,pvRequest,"");
valueNameCopy = "power.value";
testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy);
pvRecord->destroy();
}
int main(int argc,char *argv[])
{
scalarTest();
arrayTest();
powerSupplyTest();
return 0;
}

View File

@@ -1,13 +0,0 @@
TOP=../..
include $(TOP)/configure/CONFIG
PROD_HOST += testExampleRecord
testExampleRecord_SRCS += testExampleRecordMain.cpp
testExampleRecord_LIBS += pvDatabase pvAccess pvData Com
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,142 +0,0 @@
/*testExampleRecordMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <epicsStdio.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <epicsExport.h>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/pvData.h>
#include <pv/pvAccess.h>
#include <pv/powerSupplyRecordTest.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
static PVStructurePtr createPowerSupply()
{
FieldCreatePtr fieldCreate = getFieldCreate();
StandardFieldPtr standardField = getStandardField();
PVDataCreatePtr pvDataCreate = getPVDataCreate();
size_t nfields = 5;
StringArray names;
names.reserve(nfields);
FieldConstPtrArray powerSupply;
powerSupply.reserve(nfields);
names.push_back("alarm");
powerSupply.push_back(standardField->alarm());
names.push_back("timeStamp");
powerSupply.push_back(standardField->timeStamp());
String properties("alarm,display");
names.push_back("voltage");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
names.push_back("power");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
names.push_back("current");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
return pvDataCreate->createPVStructure(
fieldCreate->createStructure(names,powerSupply));
}
void test()
{
StandardPVFieldPtr standardPVField = getStandardPVField();
String properties;
ScalarType scalarType;
String recordName;
properties = "alarm,timeStamp";
scalarType = pvDouble;
recordName = "exampleDouble";
PVStructurePtr pvStructure;
pvStructure = standardPVField->scalar(scalarType,properties);
PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure);
{
pvRecord->lock();
pvRecord->process();
pvRecord->unlock();
}
cout << "processed exampleDouble " << endl;
pvRecord->destroy();
pvRecord.reset();
recordName = "powerSupplyExample";
pvStructure.reset();
pvStructure = createPowerSupply();
PowerSupplyRecordTestPtr psr =
PowerSupplyRecordTest::create(recordName,pvStructure);
if(psr.get()==NULL) {
cout << "PowerSupplyRecordTest::create failed" << endl;
return;
}
pvStructure.reset();
double voltage,power,current;
{
psr->lock();
voltage = psr->getVoltage();
power = psr->getPower();
current = psr->getCurrent();
psr->unlock();
}
cout << "initial ";
cout << " voltage " << voltage ;
cout << " power " << power;
cout << " current " << current;
cout << endl;
voltage = 1.0;
power = 1.0;
cout << "before put ";
cout << " voltage " << voltage ;
cout << " power " << power;
cout << endl;
{
psr->lock();
psr->put(power,voltage);
psr->process();
psr->unlock();
}
{
psr->lock();
cout << "after put ";
cout << " voltage " << psr->getVoltage() ;
cout << " power " << psr->getPower();
cout << " current " << psr->getCurrent();
cout << endl;
psr->unlock();
}
PVDatabasePtr pvDatabase = PVDatabase::getMaster();
pvDatabase->addRecord(psr);
psr.reset();
pvDatabase->destroy();
}
int main(int argc,char *argv[])
{
test();
return 0;
}

View File

@@ -1,29 +0,0 @@
TOP = ../..
include $(TOP)/configure/CONFIG
EXAMPLESRC = $(TOP)/example/src/
LIBRARY_IOC += pvDatabaseExample
pvDatabaseExample_LIBS += $(EPICS_BASE_IOC_LIBS)
pvDatabaseExample_LIBS += Com pvData pvAccess pvDatabase
SRC_DIRS += $(EXAMPLESRC)/exampleCounter
INC += exampleCounter.h
SRC_DIRS += $(EXAMPLESRC)/exampleServer
INC+= exampleServerCreateRecords.h
LIBSRCS += exampleServerCreateRecords.cpp
SRC_DIRS += $(EXAMPLESRC)/examplePVADoubleArrayGet
INC+= examplePVADoubleArrayGet.h
LIBSRCS += examplePVADoubleArrayGet.cpp
SRC_DIRS += $(EXAMPLESRC)/arrayPerformance
INC+= arrayPerformance.h
LIBSRCS += arrayPerformance.cpp
INC+= longArrayMonitor.h
LIBSRCS += longArrayMonitor.cpp
include $(TOP)/configure/RULES

View File

@@ -1,173 +0,0 @@
/* arrayPerformance.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.08
*/
#include <pv/lock.h>
#include <pv/arrayPerformance.h>
namespace epics { namespace pvDatabase {
using namespace epics::pvData;
using std::tr1::static_pointer_cast;
using std::tr1::dynamic_pointer_cast;
using std::cout;
using std::endl;
ArrayPerformancePtr ArrayPerformance::create(
epics::pvData::String const & recordName,
size_t size,
double delay)
{
epics::pvData::PVStructurePtr pvStructure =
epics::pvData::getStandardPVField()->scalarArray(epics::pvData::pvLong,"timeStamp,alarm");
ArrayPerformancePtr pvRecord(
new ArrayPerformance(recordName,pvStructure,size,delay));
if(!pvRecord->init()) pvRecord.reset();
return pvRecord;
}
ArrayPerformance::ArrayPerformance(
epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure,
size_t size,
double delay)
: PVRecord(recordName,pvStructure),
size(size),
delay(delay),
isDestroyed(false)
{
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
}
ArrayPerformance::~ArrayPerformance()
{
}
bool ArrayPerformance::init()
{
initPVRecord();
PVScalarArrayPtr pvScalarArray = getPVStructure()->getScalarArrayField("value",pvLong);
if(pvScalarArray==NULL) return false;
pvValue = static_pointer_cast<PVLongArray>(pvScalarArray);
ArrayPerformancePtr xxx = dynamic_pointer_cast<ArrayPerformance>(getPtrSelf());
arrayPerformanceThread = ArrayPerformanceThreadPtr(new ArrayPerformanceThread(xxx));
arrayPerformanceThread->init();
return true;
}
void ArrayPerformance::start()
{
arrayPerformanceThread->start();
}
void ArrayPerformance::process()
{
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
}
void ArrayPerformance::destroy()
{
if(isDestroyed) return;
isDestroyed = true;
arrayPerformanceThread->destroy();
arrayPerformanceThread.reset();
PVRecord::destroy();
}
ArrayPerformanceThread::ArrayPerformanceThread(ArrayPerformancePtr const & arrayPerformance)
:
arrayPerformance(arrayPerformance),
isDestroyed(false),
runReturned(false),
threadName("arrayPerformance")
{}
void ArrayPerformanceThread::init()
{
thread = std::auto_ptr<epicsThread>(new epicsThread(
*this,
threadName.c_str(),
epicsThreadGetStackSize(epicsThreadStackSmall),
epicsThreadPriorityHigh));
}
void ArrayPerformanceThread::start()
{
thread->start();
}
void ArrayPerformanceThread::destroy()
{
Lock lock(mutex);
if(isDestroyed) return;
isDestroyed = true;
while(true) {
if(runReturned) break;
lock.unlock();
epicsThreadSleep(.01);
lock.lock();
}
thread->exitWait();
thread.reset();
arrayPerformance.reset();
}
void ArrayPerformanceThread::run()
{
TimeStamp timeStamp;
TimeStamp timeStampLast;
timeStampLast.getCurrent();
int nSinceLastReport = 0;
while(true) {
if(arrayPerformance->delay>0.0) epicsThreadSleep(arrayPerformance->delay);
{
Lock lock(mutex);
if(isDestroyed) {
runReturned = true;
return;
}
}
timeStamp.getCurrent();
double diff = TimeStamp::diff(timeStamp,timeStampLast);
if(diff>=1.0) {
cout << "arrayPerformance value " << value;
cout << " time " << diff;
double iterations = nSinceLastReport;
iterations /= diff;
cout << " iterations/sec " << iterations;
double elementSize = arrayPerformance->size;
double elementsPerSecond = elementSize*nSinceLastReport;
elementsPerSecond /= diff;
elementsPerSecond /= 1e6;
cout << " elements/sec " << elementsPerSecond << "million" << endl;
cout.flush();
timeStampLast = timeStamp;
nSinceLastReport = 0;
}
++nSinceLastReport;
shared_vector<int64> xxx(arrayPerformance->size,value++);
shared_vector<const int64> data(freeze(xxx));
arrayPerformance->lock();
try {
arrayPerformance->beginGroupPut();
arrayPerformance->pvValue->replace(data);
arrayPerformance->process();
arrayPerformance->endGroupPut();
} catch(...) {
arrayPerformance->unlock();
throw;
}
arrayPerformance->unlock();
}
}
}}

View File

@@ -1,80 +0,0 @@
/* arrayPerformance.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.08
*/
#ifndef ARRAYPERFORMANCE_H
#define ARRAYPERFORMANCE_H
#include <epicsThread.h>
#include <pv/standardPVField.h>
#include <pv/timeStamp.h>
#include <pv/pvTimeStamp.h>
#include <pv/pvDatabase.h>
namespace epics { namespace pvDatabase {
class ArrayPerformance;
typedef std::tr1::shared_ptr<ArrayPerformance> ArrayPerformancePtr;
class ArrayPerformanceThread;
typedef std::tr1::shared_ptr<ArrayPerformanceThread> ArrayPerformanceThreadPtr;
class ArrayPerformance :
public PVRecord
{
public:
POINTER_DEFINITIONS(ArrayPerformance);
static ArrayPerformancePtr create(
epics::pvData::String const & recordName,
size_t size,
double delay);
virtual ~ArrayPerformance();
virtual bool init();
virtual void start();
virtual void process();
virtual void destroy();
private:
ArrayPerformance(epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure,
size_t size,
double delay);
size_t size;
double delay;
bool isDestroyed;
epics::pvData::PVLongArrayPtr pvValue;
epics::pvData::PVTimeStamp pvTimeStamp;
epics::pvData::TimeStamp timeStamp;
ArrayPerformanceThreadPtr arrayPerformanceThread;
friend class ArrayPerformanceThread;
};
class ArrayPerformanceThread :
public epicsThreadRunable
{
public:
ArrayPerformanceThread(ArrayPerformancePtr const & arrayPerformance);
virtual ~ArrayPerformanceThread(){};
void init();
void start();
virtual void run();
void destroy();
private:
ArrayPerformancePtr arrayPerformance;
bool isDestroyed;
bool runReturned;
epics::pvData::String threadName;
epics::pvData::Mutex mutex;
epics::pvData::int64 value;
std::auto_ptr<epicsThread> thread;
};
}}
#endif /* ARRAYPERFORMANCE_H */

View File

@@ -1,284 +0,0 @@
/* longArrayMonitor.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.09
*/
#include <epicsThread.h>
#include <pv/longArrayMonitor.h>
#include <pv/caProvider.h>
namespace epics { namespace pvDatabase {
using namespace epics::pvData;
using namespace epics::pvAccess;
using std::tr1::static_pointer_cast;
using std::tr1::dynamic_pointer_cast;
using std::cout;
using std::endl;
static String requesterName("longArrayMonitor");
static void messagePvt(String const & message, MessageType messageType)
{
cout << requesterName << " message " << message << endl;
}
class LAMChannelRequester :
public ChannelRequester
{
public:
LAMChannelRequester(LongArrayMonitorPtr const &longArrayMonitor)
: longArrayMonitor(longArrayMonitor)
{}
virtual ~LAMChannelRequester(){}
virtual String getRequesterName() { return requesterName;}
virtual void message(String const & message, MessageType messageType)
{ messagePvt(message,messageType);}
virtual void channelCreated(const Status& status, Channel::shared_pointer const & channel);
virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState);
private:
LongArrayMonitorPtr longArrayMonitor;
};
void LAMChannelRequester::channelCreated(const Status& status, Channel::shared_pointer const & channel)
{
if(!status.isOK()) messagePvt(status.getMessage(),errorMessage);
longArrayMonitor->status = status;
longArrayMonitor->channel = channel;
longArrayMonitor->event.signal();
}
void LAMChannelRequester::channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState)
{
MessageType messageType = (connectionState==Channel::CONNECTED ? infoMessage : errorMessage);
messagePvt("channelStateChange",messageType);
}
class LAMMonitorRequester :
public MonitorRequester,
public epicsThreadRunable
{
public:
LAMMonitorRequester(LongArrayMonitorPtr const &longArrayMonitor)
: longArrayMonitor(longArrayMonitor),
isDestroyed(false),
runReturned(false),
threadName("longArrayMonitor")
{}
virtual ~LAMMonitorRequester(){}
void init();
virtual void destroy();
virtual void run();
virtual String getRequesterName() { return requesterName;}
virtual void message(String const & message, MessageType messageType)
{ messagePvt(message,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);
private:
void handleMonitor();
LongArrayMonitorPtr longArrayMonitor;
bool isDestroyed;
bool runReturned;
epics::pvData::String threadName;
Event event;
std::auto_ptr<epicsThread> thread;
};
void LAMMonitorRequester::init()
{
thread = std::auto_ptr<epicsThread>(new epicsThread(
*this,
threadName.c_str(),
epicsThreadGetStackSize(epicsThreadStackSmall),
epicsThreadPriorityLow));
thread->start();
}
void LAMMonitorRequester::destroy()
{
if(isDestroyed) return;
isDestroyed = true;
event.signal();
while(true) {
if(runReturned) break;
epicsThreadSleep(.01);
}
thread->exitWait();
longArrayMonitor.reset();
}
void LAMMonitorRequester::monitorConnect(Status const & status,
MonitorPtr const & monitor, StructureConstPtr const & structure)
{
longArrayMonitor->status = status;
longArrayMonitor->monitor = monitor;
if(!status.isOK()) {
messagePvt(status.getMessage(),errorMessage);
longArrayMonitor->event.signal();
return;
}
bool structureOK(true);
FieldConstPtr field = structure->getField("timeStamp");
if(field==NULL) structureOK = false;
field = structure->getField("value");
if(field==NULL) {
structureOK = false;
} else {
if(field->getType()!=scalarArray) {
structureOK = false;
} else {
ScalarArrayConstPtr scalarArray = dynamic_pointer_cast<const ScalarArray>(field);
if(scalarArray->getElementType()!=pvLong) structureOK = false;
}
}
if(!structureOK) {
String message("monitorConnect: illegal structure");
messagePvt(message,errorMessage);
longArrayMonitor->status = Status(Status::STATUSTYPE_ERROR,message);
}
longArrayMonitor->event.signal();
}
void LAMMonitorRequester::run()
{
PVLongArrayPtr pvValue;
PVTimeStamp pvTimeStamp;
TimeStamp timeStamp;
TimeStamp timeStampLast;
timeStampLast.getCurrent();
while(true) {
event.wait();
if(isDestroyed) {
runReturned = true;
return;
}
while(true) {
MonitorElementPtr monitorElement = longArrayMonitor->monitor->poll();
if(monitorElement==NULL) break;
PVStructurePtr pvStructure = monitorElement->pvStructurePtr;
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
pvTimeStamp.get(timeStamp);
pvValue = dynamic_pointer_cast<PVLongArray>(pvStructure->getSubField("value"));
shared_vector<const int64> data = pvValue->view();
if(data.size()>0) {
int64 first = data[0];
int64 last = data[data.size()-1];
int64 sum = 0;
for(size_t i=0; i<data.size(); ++i) sum += data[i];
double diff = TimeStamp::diff(timeStamp,timeStampLast);
double elementsPerSecond = data.size();
elementsPerSecond = 1e-6*elementsPerSecond/diff;
cout.flush();
cout << "first " << first << " last " << last << " sum " << sum;
cout << " elements/sec " << elementsPerSecond << "million";
BitSetPtr changed = monitorElement->changedBitSet;
BitSetPtr overrun = monitorElement->overrunBitSet;
String buffer;
changed->toString(&buffer);
cout << " changed " << buffer;
buffer.clear();
overrun->toString(&buffer);
cout << " overrun " << buffer;
cout << endl;
cout.flush();
timeStampLast = timeStamp;
} else {
cout << "size = 0" << endl;
}
longArrayMonitor->monitor->release(monitorElement);
}
}
}
void LAMMonitorRequester::monitorEvent(MonitorPtr const & monitor)
{
event.signal();
}
void LAMMonitorRequester::unlisten(MonitorPtr const & monitor)
{
messagePvt("unlisten called",errorMessage);
}
LongArrayMonitorPtr LongArrayMonitor::create(
String const &providerName,
String const & channelName,
bool useQueue)
{
LongArrayMonitorPtr longArrayMonitor(new LongArrayMonitor());
if(!longArrayMonitor->init(providerName,channelName,useQueue)) longArrayMonitor.reset();
return longArrayMonitor;
}
LongArrayMonitor::LongArrayMonitor() {}
LongArrayMonitor::~LongArrayMonitor() {}
bool LongArrayMonitor::init(
String const &providerName,
String const &channelName,
bool useQueue)
{
channelRequester = LAMChannelRequesterPtr(new LAMChannelRequester(getPtrSelf()));
monitorRequester = LAMMonitorRequesterPtr(new LAMMonitorRequester(getPtrSelf()));
monitorRequester->init();
ChannelProvider::shared_pointer channelProvider = getChannelAccess()->getProvider(providerName);
if(channelProvider==NULL) {
cout << "provider " << providerName << " not found" << endl;
return false;
}
channel = channelProvider->createChannel(channelName,channelRequester,0);
event.wait();
if(!status.isOK()) return false;
String queueSize("0");
if(useQueue) queueSize="2";
String request("record[queueSize=");
request += queueSize;
request += "]field(value,timeStamp,alarm)";
PVStructurePtr pvRequest =
getCreateRequest()->createRequest(request,channelRequester);
if(pvRequest==NULL) {
cout << "request logic error " << request << endl;
return false;
}
monitor = channel->createMonitor(monitorRequester,pvRequest);
event.wait();
if(!status.isOK()) return false;
return true;
}
void LongArrayMonitor::start()
{
monitor->start();
}
void LongArrayMonitor::stop()
{
monitor->stop();
}
void LongArrayMonitor::destroy()
{
monitorRequester->destroy();
monitorRequester.reset();
monitor->destroy();
monitor.reset();
channel->destroy();
channel.reset();
channelRequester.reset();
}
}}

View File

@@ -1,71 +0,0 @@
/* longArrayMonitor.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.09
*/
#ifndef LONGARRAYMONITOR_H
#define LONGARRAYMONITOR_H
#include <pv/event.h>
#include <pv/lock.h>
#include <pv/standardPVField.h>
#include <pv/timeStamp.h>
#include <pv/pvTimeStamp.h>
#include <pv/pvAccess.h>
namespace epics { namespace pvDatabase {
class LongArrayMonitor;
typedef std::tr1::shared_ptr<LongArrayMonitor> LongArrayMonitorPtr;
class LAMChannelRequester;
typedef std::tr1::shared_ptr<LAMChannelRequester> LAMChannelRequesterPtr;
class LAMMonitorRequester;
typedef std::tr1::shared_ptr<LAMMonitorRequester> LAMMonitorRequesterPtr;
class LongArrayMonitor :
public std::tr1::enable_shared_from_this<LongArrayMonitor>
{
public:
POINTER_DEFINITIONS(LongArrayMonitor);
static LongArrayMonitorPtr create(
epics::pvData::String const & providerName,
epics::pvData::String const & channelName,
bool useQueue = false);
~LongArrayMonitor();
void start();
void stop();
void destroy();
private:
static epics::pvData::Mutex printMutex;
bool init(
epics::pvData::String const & providerName,
epics::pvData::String const & channelName,
bool useQueue);
LongArrayMonitorPtr getPtrSelf()
{
return shared_from_this();
}
LongArrayMonitor();
LAMChannelRequesterPtr channelRequester;
LAMMonitorRequesterPtr monitorRequester;
epics::pvAccess::Channel::shared_pointer channel;
epics::pvData::Monitor::shared_pointer monitor;
epics::pvData::Event event;
epics::pvData::Status status;
friend class LAMChannelRequester;
friend class LAMMonitorRequester;
};
}}
#endif /* LONGARRAYMONITOR_H */

View File

@@ -1,92 +0,0 @@
/* exampleCounter.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.04.02
*/
#ifndef EXAMPLECOUNTER_H
#define EXAMPLECOUNTER_H
#include <pv/pvDatabase.h>
#include <pv/standardPVField.h>
#include <pv/timeStamp.h>
#include <pv/pvTimeStamp.h>
namespace epics { namespace pvDatabase {
class ExampleCounter;
typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
class ExampleCounter :
public PVRecord
{
public:
POINTER_DEFINITIONS(ExampleCounter);
static ExampleCounterPtr create(
epics::pvData::String const & recordName);
virtual ~ExampleCounter();
virtual void destroy();
virtual bool init();
virtual void process();
private:
ExampleCounter(epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure);
epics::pvData::PVLongPtr pvValue;
epics::pvData::PVTimeStamp pvTimeStamp;
epics::pvData::TimeStamp timeStamp;
};
ExampleCounterPtr ExampleCounter::create(
epics::pvData::String const & recordName)
{
epics::pvData::PVStructurePtr pvStructure =
epics::pvData::getStandardPVField()->scalar(epics::pvData::pvLong,"timeStamp,alarm");
ExampleCounterPtr pvRecord(
new ExampleCounter(recordName,pvStructure));
if(!pvRecord->init()) pvRecord.reset();
return pvRecord;
}
ExampleCounter::ExampleCounter(
epics::pvData::String const & recordName,
epics::pvData::PVStructurePtr const & pvStructure)
: PVRecord(recordName,pvStructure)
{
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
}
ExampleCounter::~ExampleCounter()
{
}
void ExampleCounter::destroy()
{
PVRecord::destroy();
}
bool ExampleCounter::init()
{
initPVRecord();
epics::pvData::PVFieldPtr pvField;
pvValue = getPVStructure()->getLongField("value");
if(pvValue.get()==NULL) return false;
return true;
}
void ExampleCounter::process()
{
pvValue->put(pvValue->get() + 1.0);
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
}
}}
#endif /* EXAMPLECOUNTER_H */

View File

@@ -1,161 +0,0 @@
/* examplePVADoubleArrayGet.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.02
*/
#include <pv/examplePVADoubleArrayGet.h>
#include <pv/standardPVField.h>
#include <pv/convert.h>
namespace epics { namespace pvDatabase {
using namespace epics::pvData;
using namespace epics::pvAccess;
using std::tr1::static_pointer_cast;
using std::tr1::dynamic_pointer_cast;
using std::cout;
using std::endl;
ExamplePVADoubleArrayGetPtr ExamplePVADoubleArrayGet::create(
String const & recordName,
String const & providerName,
String const & channelName)
{
PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
pvDouble,"alarm.timeStamp");
ExamplePVADoubleArrayGetPtr pvRecord(
new ExamplePVADoubleArrayGet(
recordName,providerName,channelName,pvStructure));
if(!pvRecord->init()) pvRecord.reset();
return pvRecord;
}
ExamplePVADoubleArrayGet::ExamplePVADoubleArrayGet(
String const & recordName,
String providerName,
String channelName,
PVStructurePtr const & pvStructure)
: PVRecord(recordName,pvStructure),
providerName(providerName),
channelName(channelName),
convert(getConvert())
{
}
void ExamplePVADoubleArrayGet::destroy()
{
PVRecord::destroy();
}
bool ExamplePVADoubleArrayGet::init()
{
initPVRecord();
PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
pvAlarm.attach(pvStructure->getSubField("alarm"));
pvValue = static_pointer_cast<PVDoubleArray>(
pvStructure->getScalarArrayField("value",pvDouble));
if(pvValue==NULL) {
return false;
}
ChannelAccess::shared_pointer channelAccess = getChannelAccess();
ChannelProvider::shared_pointer provider =
channelAccess->getProvider(providerName);
if(provider==NULL) {
cout << getRecordName() << " provider "
<< providerName << " does not exist" << endl;
return false;
}
ChannelRequester::shared_pointer channelRequester =
dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
channel = provider->createChannel(channelName,channelRequester);
event.wait();
if(!status.isOK()) {
cout << getRecordName() << " createChannel failed "
<< status.getMessage() << endl;
return false;
}
ChannelGetRequester::shared_pointer channelGetRequester =
dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
PVStructurePtr pvRequest = getCreateRequest()->createRequest(
"value,alarm,timeStamp",getPtrSelf());
channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
event.wait();
if(!status.isOK()) {
cout << getRecordName() << " createChannelGet failed "
<< status.getMessage() << endl;
return false;
}
getPVValue = static_pointer_cast<PVDoubleArray>(
getPVStructure->getScalarArrayField("value",pvDouble));
if(getPVValue==NULL) {
cout << getRecordName() << " get value not PVDoubleArray" << endl;
return false;
}
return true;
}
void ExamplePVADoubleArrayGet::process()
{
status = Status::Ok;
channelGet->get(false);
event.wait();
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
AlarmSeverity severity(noAlarm);
if(!status.isOK()) {
switch(status.getType()) {
case Status::STATUSTYPE_OK: severity = noAlarm; break;
case Status::STATUSTYPE_WARNING: severity = minorAlarm; break;
case Status::STATUSTYPE_ERROR: severity = majorAlarm; break;
case Status::STATUSTYPE_FATAL: severity = invalidAlarm; break;
}
alarm.setSeverity(severity);
} else {
convert->copy(getPVValue,pvValue);
}
alarm.setMessage(status.getMessage());
pvAlarm.set(alarm);
}
void ExamplePVADoubleArrayGet::channelCreated(
const Status& status,
Channel::shared_pointer const & channel)
{
this->status = status;
this->channel = channel;
event.signal();
}
void ExamplePVADoubleArrayGet::channelStateChange(
Channel::shared_pointer const & channel,
Channel::ConnectionState connectionState)
{
}
void ExamplePVADoubleArrayGet::channelGetConnect(
const Status& status,
ChannelGet::shared_pointer const & channelGet,
PVStructure::shared_pointer const & pvStructure,
BitSet::shared_pointer const & bitSet)
{
this->status = status;
this->channelGet = channelGet;
this->getPVStructure = pvStructure;
this->bitSet = bitSet;
event.signal();
}
void ExamplePVADoubleArrayGet::getDone(const Status& status)
{
this->status = status;
event.signal();
}
}}

View File

@@ -1,82 +0,0 @@
/* examplePVADoubleArrayGet.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.02
*/
#ifndef EXAMPLEPVADOUBLEARRAYGET_H
#define EXAMPLEPVADOUBLEARRAYGET_H
#include <pv/timeStamp.h>
#include <pv/pvTimeStamp.h>
#include <pv/alarm.h>
#include <pv/pvAlarm.h>
#include <pv/pvDatabase.h>
#include <pv/pvCopy.h>
#include <pv/pvAccess.h>
#include <pv/serverContext.h>
namespace epics { namespace pvDatabase {
class ExamplePVADoubleArrayGet;
typedef std::tr1::shared_ptr<ExamplePVADoubleArrayGet> ExamplePVADoubleArrayGetPtr;
class ExamplePVADoubleArrayGet :
public PVRecord,
public epics::pvAccess::ChannelRequester,
public epics::pvAccess::ChannelGetRequester
{
public:
POINTER_DEFINITIONS(ExamplePVADoubleArrayGet);
static ExamplePVADoubleArrayGetPtr create(
epics::pvData::String const & recordName,
epics::pvData::String const & providerName,
epics::pvData::String const & channelName
);
virtual ~ExamplePVADoubleArrayGet() {}
virtual void destroy();
virtual bool init();
virtual void process();
virtual void channelCreated(
const epics::pvData::Status& status,
epics::pvAccess::Channel::shared_pointer const & channel);
virtual void channelStateChange(
epics::pvAccess::Channel::shared_pointer const & channel,
epics::pvAccess::Channel::ConnectionState connectionState);
virtual void channelGetConnect(
const epics::pvData::Status& status,
epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
epics::pvData::PVStructure::shared_pointer const & pvStructure,
epics::pvData::BitSet::shared_pointer const & bitSet);
virtual void getDone(const epics::pvData::Status& status);
private:
ExamplePVADoubleArrayGet(epics::pvData::String const & recordName,
epics::pvData::String providerName,
epics::pvData::String channelName,
epics::pvData::PVStructurePtr const & pvStructure);
epics::pvData::String providerName;
epics::pvData::String channelName;
epics::pvData::ConvertPtr convert;
epics::pvData::PVDoubleArrayPtr pvValue;
epics::pvData::PVTimeStamp pvTimeStamp;
epics::pvData::TimeStamp timeStamp;
epics::pvData::PVAlarm pvAlarm;
epics::pvData::Alarm alarm;
epics::pvAccess::Channel::shared_pointer channel;
epics::pvAccess::ChannelGet::shared_pointer channelGet;
epics::pvData::Event event;
epics::pvData::Status status;
epics::pvData::PVStructurePtr getPVStructure;
epics::pvData::BitSetPtr bitSet;
epics::pvData::PVDoubleArrayPtr getPVValue;
};
}}
#endif /* EXAMPLEPVADOUBLEARRAYGET_H */

View File

@@ -1,142 +0,0 @@
/*exampleServerCreateRecords.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.07.24
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <vector>
#include <iostream>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/powerSupplyRecordTest.h>
#include <pv/channelProviderLocal.h>
#include <pv/exampleCounter.h>
#include <pv/recordList.h>
#include <pv/traceRecord.h>
#include <pv/exampleServerCreateRecords.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
static FieldCreatePtr fieldCreate = getFieldCreate();
static StandardFieldPtr standardField = getStandardField();
static PVDataCreatePtr pvDataCreate = getPVDataCreate();
static StandardPVFieldPtr standardPVField = getStandardPVField();
static PVStructurePtr createPowerSupply()
{
size_t nfields = 5;
StringArray names;
names.reserve(nfields);
FieldConstPtrArray powerSupply;
powerSupply.reserve(nfields);
names.push_back("alarm");
powerSupply.push_back(standardField->alarm());
names.push_back("timeStamp");
powerSupply.push_back(standardField->timeStamp());
String properties("alarm,display");
names.push_back("voltage");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
names.push_back("power");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
names.push_back("current");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
return pvDataCreate->createPVStructure(
fieldCreate->createStructure(names,powerSupply));
}
static void createStructureArrayRecord(
PVDatabasePtr const &master,
ScalarType scalarType,
String const &recordName)
{
StructureConstPtr structure = standardField->scalar(
pvDouble,
String("value,alarm,timeStamp"));
StringArray names(2);
FieldConstPtrArray fields(2);
names[0] = "timeStamp";
names[1] = "value";
fields[0] = standardField->timeStamp();
fields[1] = fieldCreate->createStructureArray(structure);
StructureConstPtr top = fieldCreate->createStructure(names,fields);
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(top);
PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure);
bool result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl;
}
static void createRecords(
PVDatabasePtr const &master,
ScalarType scalarType,
String const &recordNamePrefix,
String const &properties)
{
String recordName = recordNamePrefix;
PVStructurePtr pvStructure = standardPVField->scalar(scalarType,properties);
PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure);
bool result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl;
recordName += "Array";
pvStructure = standardPVField->scalarArray(scalarType,properties);
pvRecord = PVRecord::create(recordName,pvStructure);
result = master->addRecord(pvRecord);
}
void ExampleServerCreateRecords::create()
{
PVDatabasePtr master = PVDatabase::getMaster();
PVRecordPtr pvRecord;
String recordName;
bool result(false);
recordName = "traceRecordPGRPC";
pvRecord = TraceRecord::create(recordName);
result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl;
String properties;
recordName = "exampleCounter";
pvRecord = ExampleCounter::create(recordName);
result = master->addRecord(pvRecord);
properties = "alarm,timeStamp";
createRecords(master,pvBoolean,"exampleBoolean",properties);
createRecords(master,pvByte,"exampleByte",properties);
createRecords(master,pvShort,"exampleShort",properties);
createRecords(master,pvInt,"exampleInt",properties);
createRecords(master,pvLong,"exampleLong",properties);
createRecords(master,pvFloat,"exampleFloat",properties);
createRecords(master,pvDouble,"exampleDouble",properties);
createRecords(master,pvString,"exampleString",properties);
createStructureArrayRecord(master,pvDouble,"exampleStructureArray");
recordName = "examplePowerSupply";
PVStructurePtr pvStructure = createPowerSupply();
PowerSupplyRecordTestPtr psr =
PowerSupplyRecordTest::create(recordName,pvStructure);
if(psr.get()==NULL) {
cout << "PowerSupplyRecordTest::create failed" << endl;
return;
}
result = master->addRecord(psr);
if(!result) cout<< "record " << recordName << " not added" << endl;
recordName = "laptoprecordListPGRPC";
pvRecord = RecordListRecord::create(recordName);
result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl;
}

View File

@@ -1,27 +0,0 @@
/* exampleServerCreateRecords.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.07.24
*/
#ifndef EXAMPLESERVERCREATERECORDS_H
#define EXAMPLESERVERCREATERECORDS_H
#include <pv/pvDatabase.h>
namespace epics { namespace pvDatabase {
class ExampleServerCreateRecords {
public:
static void create();
};
}}
#endif /* EXAMPLESERVERCREATERECORDS_H */

View File

@@ -1,7 +0,0 @@
TOP = ../..
include $(TOP)/configure/CONFIG
DIRS += exampleCounter
DIRS += exampleServer
DIRS += examplePVADoubleArrayGet
include $(TOP)/configure/RULES_DIRS

View File

@@ -1,21 +0,0 @@
TOP=../../../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#----------------------------------------------------
# Optimization of db files using dbst (DEFAULT: NO)
#DB_OPT = YES
#----------------------------------------------------
# Create and install (or just install)
# databases, templates, substitutions like this
DB += ai.db
#----------------------------------------------------
# If <anyname>.db template is not named <anyname>*.template add
# <anyname>_TEMPLATE = <templatename>
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,13 +0,0 @@
record(ai, "$(name)")
{
field(PREC, "1")
field(EGU, "amps")
field(HIHI, "8")
field(HIGH, "6")
field(LOW, "4")
field(LOLO, "2")
field(HHSV, "MAJOR")
field(HSV, "MINOR")
field(LSV, "MINOR")
field(LLSV, "MAJOR")
}

View File

@@ -1,8 +0,0 @@
TOP = ../../..
include $(TOP)/configure/CONFIG
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
include $(TOP)/configure/RULES_DIRS

View File

@@ -1,48 +0,0 @@
TOP=../../../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================
#==================================================
# Build an IOC support library
#
DBD += exampleCounter.dbd
LIBRARY_IOC += exampleCounterSupport
exampleCounterSupport_SRCS += exampleCounter.cpp
exampleCounterSupport_LIBS += pvData
exampleCounterSupport_LIBS += pvAccess
exampleCounterSupport_LIBS += pvDatabase
exampleCounterSupport_LIBS += pvDatabaseExample
exampleCounterSupport_LIBS += $(EPICS_BASE_IOC_LIBS)
#=============================
# build an ioc application
PROD_IOC += exampleCounter
# <name>_registerRecordDeviceDriver.cpp will be created from <name>.dbd
exampleCounter_SRCS += exampleCounter_registerRecordDeviceDriver.cpp
exampleCounter_SRCS_DEFAULT += exampleCounterMain.cpp
exampleCounter_SRCS_vxWorks += -nil-
# The following adds support from base/src/vxWorks
exampleCounter_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
exampleCounter_LIBS += pvData pvAccess
exampleCounter_LIBS += pvDatabase
exampleCounter_LIBS += pvDatabaseExample
exampleCounter_LIBS += exampleCounterSupport
exampleCounter_LIBS += $(EPICS_BASE_IOC_LIBS)
#===========================
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,66 +0,0 @@
/*exampleCounter.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.07.24
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <cantProceed.h>
#include <epicsStdio.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <iocsh.h>
#include <epicsExport.h>
#include <pv/pvIntrospect.h>
#include <pv/pvData.h>
#include <pv/pvAccess.h>
#include <pv/pvDatabase.h>
#include <pv/exampleCounter.h>
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
using std::cout;
using std::endl;
static const iocshArg testArg0 = { "recordName", iocshArgString };
static const iocshArg *testArgs[] = {
&testArg0};
static const iocshFuncDef exampleCounterFuncDef = {
"exampleCounterCreateRecord", 1, testArgs};
static void exampleCounterCallFunc(const iocshArgBuf *args)
{
char *recordName = args[0].sval;
ExampleCounterPtr record = ExampleCounter::create(recordName);
bool result = PVDatabase::getMaster()->addRecord(record);
if(!result) cout << "recordname" << " not added" << endl;
}
static void exampleCounterRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&exampleCounterFuncDef, exampleCounterCallFunc);
}
}
epicsExportRegistrar(exampleCounterRegister);

View File

@@ -1,3 +0,0 @@
include "base.dbd"
include "PVAServerRegister.dbd"
registrar("exampleCounterRegister")

View File

@@ -1,31 +0,0 @@
/* exampleCounterMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.07.24
*/
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "epicsExit.h"
#include "epicsThread.h"
#include "iocsh.h"
int main(int argc,char *argv[])
{
if(argc>=2) {
iocsh(argv[1]);
epicsThreadSleep(.2);
}
iocsh(NULL);
epicsExit(0);
return(0);
}

View File

@@ -1,21 +0,0 @@
TOP=../../../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#----------------------------------------------------
# Optimization of db files using dbst (DEFAULT: NO)
#DB_OPT = YES
#----------------------------------------------------
# Create and install (or just install)
# databases, templates, substitutions like this
DB += ai.db
#----------------------------------------------------
# If <anyname>.db template is not named <anyname>*.template add
# <anyname>_TEMPLATE = <templatename>
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,13 +0,0 @@
record(ai, "$(name)")
{
field(PREC, "1")
field(EGU, "amps")
field(HIHI, "8")
field(HIGH, "6")
field(LOW, "4")
field(LOLO, "2")
field(HHSV, "MAJOR")
field(HSV, "MINOR")
field(LSV, "MINOR")
field(LLSV, "MAJOR")
}

View File

@@ -1,8 +0,0 @@
TOP = ../../..
include $(TOP)/configure/CONFIG
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
include $(TOP)/configure/RULES_DIRS

View File

@@ -1,48 +0,0 @@
TOP=../../../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================
#==================================================
# Build an IOC support library
#
DBD += examplePVADoubleArrayGet.dbd
LIBRARY_IOC += examplePVADoubleArrayGetSupport
examplePVADoubleArrayGetSupport_SRCS += examplePVADoubleArrayGet.cpp
examplePVADoubleArrayGetSupport_LIBS += pvData
examplePVADoubleArrayGetSupport_LIBS += pvAccess
examplePVADoubleArrayGetSupport_LIBS += pvDatabase
examplePVADoubleArrayGetSupport_LIBS += pvDatabaseExample
examplePVADoubleArrayGetSupport_LIBS += $(EPICS_BASE_IOC_LIBS)
#=============================
# build an ioc application
PROD_IOC += examplePVADoubleArrayGet
# <name>_registerRecordDeviceDriver.cpp will be created from <name>.dbd
examplePVADoubleArrayGet_SRCS += examplePVADoubleArrayGet_registerRecordDeviceDriver.cpp
examplePVADoubleArrayGet_SRCS_DEFAULT += examplePVADoubleArrayGetMain.cpp
examplePVADoubleArrayGet_SRCS_vxWorks += -nil-
# The following adds support from base/src/vxWorks
examplePVADoubleArrayGet_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
examplePVADoubleArrayGet_LIBS += pvData pvAccess
examplePVADoubleArrayGet_LIBS += pvDatabase
examplePVADoubleArrayGet_LIBS += pvDatabaseExample
examplePVADoubleArrayGet_LIBS += examplePVADoubleArrayGetSupport
examplePVADoubleArrayGet_LIBS += $(EPICS_BASE_IOC_LIBS)
#===========================
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,85 +0,0 @@
/*examplePVADoubleArrayGet.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.07.24
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <cantProceed.h>
#include <epicsStdio.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <iocsh.h>
#include <epicsExport.h>
#include <pv/pvIntrospect.h>
#include <pv/pvData.h>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/pvAccess.h>
#include <pv/pvDatabase.h>
#include <pv/examplePVADoubleArrayGet.h>
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
using std::cout;
using std::endl;
static StandardPVFieldPtr standardPVField = getStandardPVField();
static const iocshArg testArg0 = { "recordName", iocshArgString };
static const iocshArg testArg1 = { "providerName", iocshArgString };
static const iocshArg testArg2 = { "channelName", iocshArgString };
static const iocshArg *testArgs[] = {
&testArg0,&testArg1,&testArg2};
static const iocshFuncDef examplePVADoubleArrayGetFuncDef = {
"examplePVADoubleArrayGetCreateRecord", 3, testArgs};
static void examplePVADoubleArrayGetCallFunc(const iocshArgBuf *args)
{
PVDatabasePtr master = PVDatabase::getMaster();
PVRecordPtr pvRecord;
bool result(false);
String recordName;
PVStructurePtr pvStructure = standardPVField->scalarArray(
pvDouble,"alarm,timeStamp");
pvRecord = PVRecord::create("doubleArray",pvStructure);
result = master->addRecord(pvRecord);
if(!result) cout<< "record " << recordName << " not added" << endl;
recordName = args[0].sval;
char *providerName = args[1].sval;
char *channelName = args[2].sval;
ExamplePVADoubleArrayGetPtr record = ExamplePVADoubleArrayGet::create(recordName,providerName,channelName);
if(record!=NULL)
result = master->addRecord(record);
if(!result) cout << "recordname" << " not added" << endl;
}
static void examplePVADoubleArrayGetRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&examplePVADoubleArrayGetFuncDef, examplePVADoubleArrayGetCallFunc);
}
}
epicsExportRegistrar(examplePVADoubleArrayGetRegister);

View File

@@ -1,4 +0,0 @@
include "base.dbd"
include "PVAServerRegister.dbd"
include "PVAClientRegister.dbd"
registrar("examplePVADoubleArrayGetRegister")

View File

@@ -1,31 +0,0 @@
/* examplePVADoubleArrayGetMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.07.24
*/
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "epicsExit.h"
#include "epicsThread.h"
#include "iocsh.h"
int main(int argc,char *argv[])
{
if(argc>=2) {
iocsh(argv[1]);
epicsThreadSleep(.2);
}
iocsh(NULL);
epicsExit(0);
return(0);
}

View File

@@ -1,28 +0,0 @@
TOP=../../../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#----------------------------------------------------
# Optimization of db files using dbst (DEFAULT: NO)
#DB_OPT = YES
#----------------------------------------------------
# Create and install (or just install)
# databases, templates, substitutions like this
DB += dbScalar.db
DB += dbInteger.db
DB += dbArray.db
DB += dbString.db
DB += dbStringArray.db
DB += dbEnum.db
DB += dbCounter.db
#----------------------------------------------------
# If <anyname>.db template is not named <anyname>*.template add
# <anyname>_TEMPLATE = <templatename>
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,9 +0,0 @@
record(waveform, "$(name)")
{
field(NELM,"5")
field(NORD,"5")
field(FTVL,"$(type)")
field(EGU, "Counts")
field(HOPR, "10")
field(LOPR, "0")
}

View File

@@ -1,20 +0,0 @@
record(calc, "${name}")
{
field(DESC, "Counter")
field(SCAN,"1 second")
field(CALC, "(A<B)?(A+C):D")
field(INPA, "${name} NPP NMS")
field(INPB, "9")
field(INPC, "1")
field(INPD, "0")
field(EGU, "Counts")
field(HOPR, "10")
field(HIHI, "8")
field(HIGH, "6")
field(LOW, "4")
field(LOLO, "2")
field(HHSV, "MAJOR")
field(HSV, "MINOR")
field(LSV, "MINOR")
field(LLSV, "MAJOR")
}

View File

@@ -1,14 +0,0 @@
record(mbbo, "$(name)")
{
field(NOBT,"2")
field(ZRVL,"0")
field(ONVL,"1")
field(TWVL,"2")
field(THVL,"3")
field(ZRST,"zero")
field(ONST,"one")
field(TWST,"two")
field(THST,"three")
field(TWSV,"MINOR")
field(THSV,"MAJOR")
}

View File

@@ -1,16 +0,0 @@
record($(type), "$(name)")
{
field(EGU, "Counts")
field(HOPR, "10")
field(LOPR, "0")
field(DRVH, "9")
field(DRVL, "0")
field(HIHI, "8")
field(HIGH, "6")
field(LOW, "4")
field(LOLO, "2")
field(HHSV, "MAJOR")
field(HSV, "MINOR")
field(LSV, "MINOR")
field(LLSV, "MAJOR")
}

View File

@@ -1,17 +0,0 @@
record($(type), "$(name)")
{
field(PREC, "1")
field(EGU, "Counts")
field(HOPR, "10")
field(LOPR, "0")
field(DRVH, "9.9")
field(DRVL, "-0.1")
field(HIHI, "8")
field(HIGH, "6")
field(LOW, "4")
field(LOLO, "2")
field(HHSV, "MAJOR")
field(HSV, "MINOR")
field(LSV, "MINOR")
field(LLSV, "MAJOR")
}

View File

@@ -1,3 +0,0 @@
record(stringout, "$(name)")
{
}

View File

@@ -1,5 +0,0 @@
record(waveform, "${name}")
{
field(NELM,"5")
field(FTVL,"STRING")
}

View File

@@ -1,8 +0,0 @@
TOP = ../../..
include $(TOP)/configure/CONFIG
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*))
DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*))
include $(TOP)/configure/RULES_DIRS

View File

@@ -1,48 +0,0 @@
TOP=../../../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================
#==================================================
# Build an IOC support library
#
DBD += exampleServer.dbd
LIBRARY_IOC += exampleServerSupport
exampleServerSupport_SRCS += exampleServer.cpp
exampleServerSupport_LIBS += pvData
exampleServerSupport_LIBS += pvAccess
exampleServerSupport_LIBS += pvDatabase
exampleServerSupport_LIBS += pvDatabaseExample
exampleServerSupport_LIBS += $(EPICS_BASE_IOC_LIBS)
#=============================
# build an ioc application
PROD_IOC += exampleServer
# <name>_registerRecordDeviceDriver.cpp will be created from <name>.dbd
exampleServer_SRCS += exampleServer_registerRecordDeviceDriver.cpp
exampleServer_SRCS_DEFAULT += exampleServerMain.cpp
exampleServer_SRCS_vxWorks += -nil-
# The following adds support from base/src/vxWorks
exampleServer_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
exampleServer_LIBS += pvData pvAccess
exampleServer_LIBS += pvDatabase
exampleServer_LIBS += pvDatabaseExample
exampleServer_LIBS += exampleServerSupport
exampleServer_LIBS += $(EPICS_BASE_IOC_LIBS)
#===========================
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -1,61 +0,0 @@
/*exampleServer.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.07.24
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <cantProceed.h>
#include <epicsStdio.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <iocsh.h>
#include <epicsExport.h>
#include <pv/pvIntrospect.h>
#include <pv/pvData.h>
#include <pv/pvAccess.h>
#include <pv/pvDatabase.h>
#include <pv/exampleServerCreateRecords.h>
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
static const iocshArg testArg0 = { "prefix", iocshArgString };
static const iocshArg *testArgs[] = {
&testArg0};
static const iocshFuncDef exampleServerFuncDef = {
"exampleServerCreateRecords", 1, testArgs};
static void exampleServerCallFunc(const iocshArgBuf *args)
{
char *prefix = args[0].sval;
ExampleServerCreateRecords::create();
}
static void exampleServerRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&exampleServerFuncDef, exampleServerCallFunc);
}
}
epicsExportRegistrar(exampleServerRegister);

View File

@@ -1,3 +0,0 @@
include "base.dbd"
include "PVAServerRegister.dbd"
registrar("exampleServerRegister")

View File

@@ -1,31 +0,0 @@
/* exampleServerMain.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.07.24
*/
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "epicsExit.h"
#include "epicsThread.h"
#include "iocsh.h"
int main(int argc,char *argv[])
{
if(argc>=2) {
iocsh(argv[1]);
epicsThreadSleep(.2);
}
iocsh(NULL);
epicsExit(0);
return(0);
}

View File

@@ -1,7 +0,0 @@
TOP = ..
include $(TOP)/configure/CONFIG
DIRS += $(wildcard *ioc*)
DIRS += $(wildcard as*)
DIRS += $(wildcard example*)
include $(EPICS_BASE)/configure/RULES_DIRS

View File

@@ -1,5 +0,0 @@
TOP = ../..
include $(TOP)/configure/CONFIG
ARCH = $(EPICS_HOST_ARCH)
TARGETS = envPaths
include $(TOP)/configure/RULES.ioc

View File

@@ -1,17 +0,0 @@
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase("dbd/exampleCounter.dbd")
exampleCounter_registerRecordDeviceDriver(pdbbase)
## Load record instances
dbLoadRecords("db/ai.db","name=double01")
cd ${TOP}/iocBoot/${IOC}
iocInit()
epicsThreadSleep(2.0)
casr
exampleCounterCreateRecord exampleCounter
startPVAServer

View File

@@ -1,5 +0,0 @@
TOP = ../..
include $(TOP)/configure/CONFIG
ARCH = $(EPICS_HOST_ARCH)
TARGETS = envPaths
include $(TOP)/configure/RULES.ioc

View File

@@ -1,16 +0,0 @@
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase("dbd/examplePVADoubleArrayGet.dbd")
examplePVADoubleArrayGet_registerRecordDeviceDriver(pdbbase)
## Load record instances
dbLoadRecords("db/dbArray.db","name=double01,type=DOUBLE")
cd ${TOP}/iocBoot/${IOC}
iocInit()
startPVAClient
startPVAServer
examplePVADoubleArrayGetCreateRecord examplePVADoubleArrayGet local doubleArray

View File

@@ -1,16 +0,0 @@
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase("dbd/examplePVADoubleArrayGet.dbd")
examplePVADoubleArrayGet_registerRecordDeviceDriver(pdbbase)
## Load record instances
dbLoadRecords("db/dbArray.db","name=double01,type=DOUBLE")
cd ${TOP}/iocBoot/${IOC}
iocInit()
startPVAClient
startPVAServer
examplePVADoubleArrayGetCreateRecord examplePVADoubleArrayGet pvAccess arrayDouble

View File

@@ -1,5 +0,0 @@
TOP = ../..
include $(TOP)/configure/CONFIG
ARCH = $(EPICS_HOST_ARCH)
TARGETS = envPaths
include $(TOP)/configure/RULES.ioc

View File

@@ -1,20 +0,0 @@
< envPaths
cd ${TOP}
## Register all support components
dbLoadDatabase("dbd/exampleServer.dbd")
exampleServer_registerRecordDeviceDriver(pdbbase)
## Load record instances
dbLoadRecords("db/dbScalar.db","name=double01,type=ao")
dbLoadRecords("db/dbStringArray.db","name=stringArray01")
dbLoadRecords("db/dbEnum.db","name=enum01")
dbLoadRecords("db/dbCounter.db","name=counter01");
cd ${TOP}/iocBoot/${IOC}
iocInit()
epicsThreadSleep(2.0)
casr
exampleServerCreateRecords
startPVAServer

89
jenkins/cloudbees_build Normal file
View File

@@ -0,0 +1,89 @@
# pvDatabase C++ implementation
# Jenkins @ Cloudbees build script
#
# Jenkins invokes scripts with the "-ex" option. So the build is considered a failure
# if any of the commands exits with a non-zero exit code.
#
# Author: Ralph Lange <ralph.lange@gmx.de>
# Copyright (C) 2014 Helmholtz-Zentrum Berlin für Materialien und Energie GmbH
# Copyright (C) 2014-2016 ITER Organization.
# All rights reserved. Use is subject to license terms.
installTool () {
local module=$1
local version=$2
wget -nv https://openepics.ci.cloudbees.com/job/${module}-${version}_Build/lastSuccessfulBuild/artifact/${module,,}-${version}.CB-dist.tar.gz
tar -xzf ${module,,}-${version}.CB-dist.tar.gz
}
installE4 () {
local module=$1
local branch=$2
# If microbench version does not exist, try without
if [ "${MB}" = "WITH_MICROBENCH" ]; then
if ! wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=WITH_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz; then
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
fi
else
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
fi
tar -xzf ${module}.CB-dist.tar.gz
}
###########################################
# Defaults for EPICS Base and MB
DEFAULT_BASE=3.15.4
BASE=${BASE:-${DEFAULT_BASE}}
MB=${MB:-"NO_MICROBENCH"}
###########################################
# Dependent module branches
PVCOMMON_BRANCH="release-4.2"
PVDATA_BRANCH="release-6.0"
PVACCESS_BRANCH="release-5.0"
###########################################
# Fetch and unpack dependencies
export STUFF=/tmp/stuff
rm -fr ${STUFF}
mkdir -p ${STUFF}
cd ${STUFF}
installTool Boost 1.61.0
installTool Base ${BASE}
installE4 pvCommon ${PVCOMMON_BRANCH}
installE4 pvData ${PVDATA_BRANCH}
installE4 pvAccess ${PVACCESS_BRANCH}
###########################################
# Build
cd ${WORKSPACE}
export EPICS_BASE=${STUFF}
export EPICS_HOST_ARCH=$(${EPICS_BASE}/startup/EpicsHostArch)
export LD_LIBRARY_PATH=${EPICS_BASE}/lib/${EPICS_HOST_ARCH}
export PATH=${STUFF}/bin:${PATH}
cat > configure/RELEASE.local << EOF
EPICS_BASE=${EPICS_BASE}
EOF
make distclean all
###########################################
# Test
make runtests
###########################################
# Create distribution
tar -czf pvDatabase.CB-dist.tar.gz lib include dbd LICENSE

74
jenkins/cloudbees_doc Normal file
View File

@@ -0,0 +1,74 @@
# pvDatabase C++ implementation
# Jenkins @ Cloudbees documentation generation and deployment
#
# Jenkins invokes scripts with the "-ex" option. So the build is considered a failure
# if any of the commands exits with a non-zero exit code.
#
# Author: Ralph Lange <ralph.lange@gmx.de>
# Copyright (C) 2014 Helmholtz-Zentrum Berlin für Materialien und Energie GmbH
# Copyright (C) 2014-2016 ITER Organization.
# All rights reserved. Use is subject to license terms.
installTool () {
local module=$1
local version=$2
wget -nv https://openepics.ci.cloudbees.com/job/${module}-${version}_Build/lastSuccessfulBuild/artifact/${module,,}-${version}.CB-dist.tar.gz
tar -xzf ${module,,}-${version}.CB-dist.tar.gz
}
installE4 () {
local module=$1
local branch=$2
# If microbench version does not exist, try without
if [ "${MB}" = "WITH_MICROBENCH" ]; then
if ! wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=WITH_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz; then
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
fi
else
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
fi
tar -xzf ${module}.CB-dist.tar.gz
}
###########################################
# Defaults for EPICS Base and parameters
BASE=3.15.4
PUBLISH=${PUBLISH:-NO}
BRANCH=${BRANCH:-master}
MB=NO_MICROBENCH
###########################################
# Fetch and unpack dependencies
export STUFF=/tmp/stuff
rm -fr ${STUFF}
mkdir -p ${STUFF}
cd ${STUFF}
installTool Doxygen 1.8.11
###########################################
# Generate
cd ${WORKSPACE}
installE4 pvDatabase ${BRANCH}
export PATH=${STUFF}/bin:${PATH}
doxygen
###########################################
# Publish
if [ "${PUBLISH}" != "DONT" ]; then
# Upload explicit dummy to ensure target directory exists
echo "Created by CloudBees Jenkins upload job. Should be deleted as part of the job." > DUMMY
rsync -q -e ssh DUMMY epics-jenkins@web.sourceforge.net:/home/project-web/epics-pvdata/htdocs/docbuild/pvDatabaseCPP/${PUBLISH}/
rsync -aqP --delete -e ssh documentation epics-jenkins@web.sourceforge.net:/home/project-web/epics-pvdata/htdocs/docbuild/pvDatabaseCPP/${PUBLISH}/
fi

View File

@@ -1,42 +1,30 @@
# Makefile for the pvDatabase library
TOP = ..
include $(TOP)/configure/CONFIG
DATABASE = $(TOP)/src/
PVDATABASE_SRC = $(TOP)/src
LIBRARY_IOC += pvDatabase
LIBRARY += pvDatabase
# shared library ABI version.
SHRLIB_VERSION ?= 4.2.0
INC += pv/channelProviderLocal.h
INC += pv/pvDatabase.h
INC += pv/traceRecord.h
INC += pv/removeRecord.h
include $(PVDATABASE_SRC)/database/Makefile
include $(PVDATABASE_SRC)/pvAccess/Makefile
include $(PVDATABASE_SRC)/special/Makefile
pvDatabase_LIBS += pvAccess pvData Com
pvDatabase_LIBS += $(EPICS_BASE_IOC_LIBS)
pvDatabase_LIBS += Com pvData pvAccess
SRC_DIRS += $(DATABASE)/pvData
INC += pvSubArrayCopy.h
LIBSRCS += pvSubArrayCopy.cpp
SRC_DIRS += $(DATABASE)/database
INC += pvDatabase.h
LIBSRCS += pvRecord.cpp
LIBSRCS += pvDatabase.cpp
SRC_DIRS += $(DATABASE)/pvAccess
INC += channelProviderLocal.h
INC += pvCopy.h
INC += monitorAlgorithm.h
LIBSRCS += channelProviderLocal.cpp
LIBSRCS += pvCopy.cpp
LIBSRCS += channelLocal.cpp
LIBSRCS += monitorFactory.cpp
SRC_DIRS += $(DATABASE)/V3IOC
DBD += PVAServerRegister.dbd
DBD += PVAClientRegister.dbd
LIBSRCS += PVAServerRegister.cpp
LIBSRCS += PVAClientRegister.cpp
SRC_DIRS += $(DATABASE)/special
INC += recordList.h
INC += traceRecord.h
INC += powerSupplyRecordTest.h
LIBSRCS += recordList.cpp
LIBSRCS += traceRecord.cpp
include $(TOP)/configure/RULES

View File

@@ -1,72 +0,0 @@
/*PVAClientRegister.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.08.05
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <iocsh.h>
#include <epicsExport.h>
#include <pv/pvAccess.h>
#include <pv/clientFactory.h>
using std::cout;
using std::endl;
using namespace epics::pvData;
using namespace epics::pvAccess;
static const iocshFuncDef startPVAClientFuncDef = {
"startPVAClient", 0, 0
};
extern "C" void startPVAClient(const iocshArgBuf *args)
{
ClientFactory::start();
}
static const iocshFuncDef stopPVAClientFuncDef = {
"stopPVAClient", 0, 0
};
extern "C" void stopPVAClient(const iocshArgBuf *args)
{
ClientFactory::stop();
}
static void startPVAClientRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&startPVAClientFuncDef, startPVAClient);
}
}
static void stopPVAClientRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&stopPVAClientFuncDef, stopPVAClient);
}
}
epicsExportRegistrar(startPVAClientRegister);
epicsExportRegistrar(stopPVAClientRegister);

View File

@@ -1,2 +0,0 @@
registrar("startPVAClientRegister")
registrar("stopPVAClientRegister")

View File

@@ -1,155 +0,0 @@
/*PVAServerRegister.cpp */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.07.24
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <cantProceed.h>
#include <epicsStdio.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <iocsh.h>
#include <epicsExport.h>
#include <pv/pvAccess.h>
#include <pv/serverContext.h>
#include <pv/channelProviderLocal.h>
using std::cout;
using std::endl;
using namespace epics::pvData;
using namespace epics::pvAccess;
using namespace epics::pvDatabase;
class PVAServerCTX;
typedef std::tr1::shared_ptr<PVAServerCTX> PVAServerCTXPtr;
class PVAServerCTX :
public std::tr1::enable_shared_from_this<PVAServerCTX>
{
public:
POINTER_DEFINITIONS(PVAServerCTX);
static PVAServerCTXPtr getPVAServerCTX();
void start();
void stop();
virtual ~PVAServerCTX() {}
private:
PVAServerCTX() {}
shared_pointer getPtrSelf()
{
return shared_from_this();
}
ServerContext::shared_pointer ctx;
};
void PVAServerCTX::start()
{
if(ctx!=NULL) {
cout<< "PVAServer already started" << endl;
return;
}
ctx = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
}
void PVAServerCTX::stop()
{
if(ctx==NULL) {
cout<< "PVAServer already stopped" << endl;
return;
}
ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
ctx->destroy();
ctx.reset();
epicsThreadSleep(1.0);
channelProvider->destroy();
}
PVAServerCTXPtr PVAServerCTX::getPVAServerCTX()
{
static PVAServerCTXPtr pvPVAServerCTX;
static Mutex mutex;
Lock xx(mutex);
if(pvPVAServerCTX==NULL) {
pvPVAServerCTX = PVAServerCTXPtr(new PVAServerCTX());
}
return pvPVAServerCTX;
}
static const iocshFuncDef startPVAServerFuncDef = {
"startPVAServer", 0, 0
};
extern "C" void startPVAServer(const iocshArgBuf *args)
{
PVAServerCTX::getPVAServerCTX()->start();
}
static const iocshFuncDef stopPVAServerFuncDef = {
"stopPVAServer", 0, 0
};
extern "C" void stopPVAServer(const iocshArgBuf *args)
{
PVAServerCTX::getPVAServerCTX()->stop();
}
static const iocshFuncDef pvdblFuncDef = {
"pvdbl", 0, 0
};
extern "C" void pvdbl(const iocshArgBuf *args)
{
PVDatabasePtr master = PVDatabase::getMaster();
PVStringArrayPtr pvNames = master->getRecordNames();
PVStringArray::const_svector xxx = pvNames->view();
for(size_t i=0; i<xxx.size(); ++i) cout<< xxx[i] << endl;
}
static void startPVAServerRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&startPVAServerFuncDef, startPVAServer);
}
}
static void stopPVAServerRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&stopPVAServerFuncDef, stopPVAServer);
}
}
static void pvdblRegister(void)
{
static int firstTime = 1;
if (firstTime) {
firstTime = 0;
iocshRegister(&pvdblFuncDef, pvdbl);
getChannelProviderLocal();
}
}
epicsExportRegistrar(startPVAServerRegister);
epicsExportRegistrar(stopPVAServerRegister);
epicsExportRegistrar(pvdblRegister);

View File

@@ -1,3 +0,0 @@
registrar("startPVAServerRegister")
registrar("stopPVAServerRegister")
registrar("pvdblRegister")

6
src/database/Makefile Normal file
View File

@@ -0,0 +1,6 @@
# This is a Makefile fragment, see ../Makefile
SRC_DIRS += $(PVDATABASE_SRC)/database
LIBSRCS += pvRecord.cpp
LIBSRCS += pvDatabase.cpp

View File

@@ -9,6 +9,10 @@
* @date 2012.11.21
*/
#include <epicsGuard.h>
#define epicsExportSharedSymbols
#include <pv/pvDatabase.h>
using std::tr1::static_pointer_cast;
@@ -35,30 +39,25 @@ PVDatabase::PVDatabase()
PVDatabase::~PVDatabase()
{
destroy();
}
void PVDatabase::destroy()
{
lock();
try {
{
epicsGuard<epics::pvData::Mutex> guard(mutex);
if(isDestroyed) {
unlock();
return;
}
isDestroyed = true;
PVRecordMap::iterator iter;
while(true) {
iter = recordMap.begin();
if(iter==recordMap.end()) break;
PVRecordPtr pvRecord = (*iter).second;
recordMap.erase(iter);
if(pvRecord.get()!=NULL) pvRecord->destroy();
}
unlock();
} catch (...) {
unlock();
throw;
}
for(PVRecordMap::iterator iter = recordMap.begin(); iter != recordMap.end(); iter++) {
PVRecordPtr pvRecord = (*iter).second;
if(pvRecord) {
pvRecord->destroy();
}
}
recordMap.clear();
}
void PVDatabase::lock() {
@@ -69,113 +68,72 @@ void PVDatabase::unlock() {
mutex.unlock();
}
PVRecordPtr PVDatabase::findRecord(String const& recordName)
PVRecordPtr PVDatabase::findRecord(string const& recordName)
{
lock();
try {
PVRecordPtr xxx;
if(isDestroyed) {
unlock();
return xxx;
}
PVRecordMap::iterator iter = recordMap.find(recordName);
if(iter!=recordMap.end()) {
unlock();
return (*iter).second;
}
unlock();
epicsGuard<epics::pvData::Mutex> guard(mutex);
PVRecordPtr xxx;
if(isDestroyed) {
return xxx;
} catch(...) {
unlock();
throw;
}
}
PVStringArrayPtr PVDatabase::getRecordNames()
{
lock();
try {
PVStringArrayPtr xxx;
if(isDestroyed) {
unlock();
return xxx;
}
PVStringArrayPtr pvStringArray = static_pointer_cast<PVStringArray>
(getPVDataCreate()->createPVScalarArray(pvString));
size_t len = recordMap.size();
shared_vector<String> names(len);
PVRecordMap::iterator iter;
size_t i = 0;
for(iter = recordMap.begin(); iter!=recordMap.end(); ++iter) {
names[i++] = (*iter).first;
}
shared_vector<const String> temp(freeze(names));
pvStringArray->replace(temp);
unlock();
return pvStringArray;
} catch(...) {
unlock();
throw;
PVRecordMap::iterator iter = recordMap.find(recordName);
if(iter!=recordMap.end()) {
return (*iter).second;
}
return xxx;
}
bool PVDatabase::addRecord(PVRecordPtr const & record)
{
lock();
try {
if(isDestroyed) {
unlock();
return false;
}
String recordName = record->getRecordName();
PVRecordMap::iterator iter = recordMap.find(recordName);
if(iter!=recordMap.end()) {
unlock();
return false;
}
record->start();
recordMap.insert(PVRecordMap::value_type(recordName,record));
unlock();
return true;
} catch(...) {
unlock();
throw;
epicsGuard<epics::pvData::Mutex> guard(mutex);
if(isDestroyed) {
return false;
}
string recordName = record->getRecordName();
PVRecordMap::iterator iter = recordMap.find(recordName);
if(iter!=recordMap.end()) {
return false;
}
record->start();
recordMap.insert(PVRecordMap::value_type(recordName,record));
return true;
}
bool PVDatabase::removeRecord(PVRecordPtr const & record)
{
lock();
try {
if(isDestroyed) {
unlock();
return false;
}
String recordName = record->getRecordName();
PVRecordMap::iterator iter = recordMap.find(recordName);
if(iter!=recordMap.end()) {
PVRecordPtr pvRecord = (*iter).second;
recordMap.erase(iter);
if(pvRecord.get()!=NULL) pvRecord->destroy();
unlock();
return true;
}
unlock();
epicsGuard<epics::pvData::Mutex> guard(mutex);
if(isDestroyed) {
return false;
} catch(...) {
unlock();
throw;
}
string recordName = record->getRecordName();
PVRecordMap::iterator iter = recordMap.find(recordName);
if(iter!=recordMap.end()) {
PVRecordPtr pvRecord = (*iter).second;
recordMap.erase(iter);
if(pvRecord) pvRecord->destroy();
return true;
}
return false;
}
String PVDatabase::getRequesterName()
{
static String name("masterDatabase");
return name;
}
void PVDatabase::message(String const & message,MessageType messageType)
PVStringArrayPtr PVDatabase::getRecordNames()
{
epicsGuard<epics::pvData::Mutex> guard(mutex);
PVStringArrayPtr xxx;
if(isDestroyed) {
return xxx;
}
PVStringArrayPtr pvStringArray = static_pointer_cast<PVStringArray>
(getPVDataCreate()->createPVScalarArray(pvString));
size_t len = recordMap.size();
shared_vector<string> names(len);
PVRecordMap::iterator iter;
size_t i = 0;
for(iter = recordMap.begin(); iter!=recordMap.end(); ++iter) {
names[i++] = (*iter).first;
}
shared_vector<const string> temp(freeze(names));
pvStringArray->replace(temp);
return pvStringArray;
}
}}

View File

@@ -8,9 +8,13 @@
* @author mrk
* @date 2012.11.21
*/
#include <epicsGuard.h>
#include <epicsThread.h>
#define epicsExportSharedSymbols
#include <pv/pvDatabase.h>
#include <epicsThread.h>
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
@@ -19,12 +23,11 @@ using namespace std;
namespace epics { namespace pvDatabase {
PVRecordPtr PVRecord::create(
String const &recordName,
string const &recordName,
PVStructurePtr const & pvStructure)
{
PVRecordPtr pvRecord(new PVRecord(recordName,pvStructure));
if(!pvRecord->init()) {
pvRecord->destroy();
pvRecord.reset();
}
return pvRecord;
@@ -32,150 +35,95 @@ PVRecordPtr PVRecord::create(
PVRecord::PVRecord(
String const & recordName,
string const & recordName,
PVStructurePtr const & pvStructure)
: recordName(recordName),
pvStructure(pvStructure),
convert(getConvert()),
depthGroupPut(0),
traceLevel(0),
isDestroyed(false)
isDestroyed(false),
isAddListener(false)
{
}
PVRecord::~PVRecord()
{
if(traceLevel>1) {
if(traceLevel>0) {
cout << "~PVRecord() " << recordName << endl;
}
destroy();
}
void PVRecord::initPVRecord()
{
PVRecordStructurePtr parent;
pvRecordStructure = PVRecordStructurePtr(
new PVRecordStructure(pvStructure,parent,getPtrSelf()));
new PVRecordStructure(pvStructure,parent,shared_from_this()));
pvRecordStructure->init();
pvStructure->setRequester(getPtrSelf());
PVFieldPtr pvField = pvStructure->getSubField("timeStamp");
if(pvField) pvTimeStamp.attach(pvField);
}
void PVRecord::destroy()
{
if(traceLevel>1) {
cout << "PVRecord::destroy() " << recordName << endl;
}
lock();
try {
{
epicsGuard<epics::pvData::Mutex> guard(mutex);
if(traceLevel>0) {
cout << "PVRecord::destroy() " << recordName
<< " isDestroyed " << (isDestroyed ? "true" : "false")
<< endl;
}
if(isDestroyed) {
unlock();
return;
}
isDestroyed = true;
std::list<RequesterPtr>::iterator requesterIter;
while(true) {
requesterIter = requesterList.begin();
if(requesterIter==requesterList.end()) break;
requesterList.erase(requesterIter);
unlock();
(*requesterIter)->message("record destroyed",fatalErrorMessage);
lock();
}
PVDatabasePtr pvDatabase(PVDatabase::getMaster());
if(pvDatabase) pvDatabase->removeRecord(shared_from_this());
pvTimeStamp.detach();
for(std::list<PVListenerWPtr>::iterator iter = pvListenerList.begin();
iter!=pvListenerList.end();
iter++ )
{
PVListenerPtr listener = iter->lock();
if(!listener) continue;
if(traceLevel>0) {
cout << "PVRecord::destroy() calling listener->unlisten " << recordName << endl;
}
std::list<PVRecordClientPtr>::iterator clientIter;
while(true) {
clientIter = pvRecordClientList.begin();
if(clientIter==pvRecordClientList.end()) break;
pvRecordClientList.erase(clientIter);
unlock();
(*clientIter)->detach(getPtrSelf());
lock();
listener->unlisten(shared_from_this());
}
pvListenerList.clear();
for (std::list<PVRecordClientPtr>::iterator iter = clientList.begin();
iter!=clientList.end();
iter++ )
{
PVRecordClientPtr client = *iter;
if(!client) continue;
if(traceLevel>0) {
cout << "PVRecord::destroy() calling client->detach " << recordName << endl;
}
std::list<PVListenerPtr>::iterator listenerIter;
while(true) {
listenerIter = pvListenerList.begin();
if(listenerIter==pvListenerList.end()) break;
pvListenerList.erase(listenerIter);
unlock();
(*listenerIter)->unlisten(getPtrSelf());
lock();
}
pvRecordStructure->destroy();
pvRecordStructure.reset();
convert.reset();
pvStructure.reset();
unlock();
} catch(...) {
unlock();
throw;
client->detach(shared_from_this());
}
clientList.clear();
}
void PVRecord::process()
{
if(traceLevel>2) {
cout << "PVRecord::process() " << recordName << endl;
}
if(pvTimeStamp.isAttached()) {
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
}
}
String PVRecord::getRecordName() {return recordName;}
PVRecordStructurePtr PVRecord::getPVRecordStructure() {return pvRecordStructure;}
PVStructurePtr PVRecord::getPVStructure() {return pvStructure;}
PVRecordFieldPtr PVRecord::findPVRecordField(PVFieldPtr const & pvField)
{
return findPVRecordField(pvRecordStructure,pvField);
}
bool PVRecord::addRequester(epics::pvData::RequesterPtr const & requester)
{
if(traceLevel>2) {
cout << "PVRecord::addRequester() " << recordName << endl;
}
lock();
try {
if(isDestroyed) {
unlock();
return false;
}
std::list<RequesterPtr>::iterator iter;
for (iter = requesterList.begin(); iter!=requesterList.end(); iter++ ) {
if((*iter).get()==requester.get()) {
unlock();
return false;
}
}
requesterList.push_back(requester);
unlock();
return true;
} catch (...) {
unlock();
throw;
}
}
bool PVRecord::removeRequester(epics::pvData::RequesterPtr const & requester)
{
if(traceLevel>2) {
cout << "PVRecord::removeRequester() " << recordName << endl;
}
lock();
try {
if(isDestroyed) {
unlock();
return false;
}
std::list<RequesterPtr>::iterator iter;
for (iter = requesterList.begin(); iter!=requesterList.end(); iter++ ) {
if((*iter).get()==requester.get()) {
requesterList.erase(iter);
unlock();
return true;
}
}
unlock();
return false;
} catch (...) {
unlock();
throw;
}
}
PVRecordFieldPtr PVRecord::findPVRecordField(
PVRecordStructurePtr const & pvrs,
PVFieldPtr const & pvField)
@@ -239,147 +187,120 @@ void PVRecord::lockOtherRecord(PVRecordPtr const & otherRecord)
bool PVRecord::addPVRecordClient(PVRecordClientPtr const & pvRecordClient)
{
if(traceLevel>2) {
if(traceLevel>1) {
cout << "PVRecord::addPVRecordClient() " << recordName << endl;
}
lock();
try {
if(isDestroyed) {
unlock();
epicsGuard<epics::pvData::Mutex> guard(mutex);
if(isDestroyed) {
return false;
}
std::list<PVRecordClientPtr>::iterator iter;
for (iter = clientList.begin();
iter!=clientList.end();
iter++ )
{
PVRecordClientPtr client = *iter;
if(client==pvRecordClient) {
return false;
}
std::list<PVRecordClientPtr>::iterator iter;
for (iter = pvRecordClientList.begin();
iter!=pvRecordClientList.end();
iter++ )
{
if((*iter).get()==pvRecordClient.get()) {
unlock();
return false;
}
}
pvRecordClientList.push_back(pvRecordClient);
unlock();
return true;
} catch (...) {
unlock();
throw;
}
if(traceLevel>1) {
cout << "PVRecord::addPVRecordClient() calling clientList.push_back(pvRecordClient)" << recordName << endl;
}
clientList.push_back(pvRecordClient);
return true;
}
bool PVRecord::removePVRecordClient(PVRecordClientPtr const & pvRecordClient)
{
if(traceLevel>2) {
cout << "PVRecord::removePVRecordClient() " << recordName << endl;
}
lock();
try {
if(isDestroyed) {
unlock();
return false;
}
std::list<PVRecordClientPtr>::iterator iter;
for (iter = pvRecordClientList.begin();
iter!=pvRecordClientList.end();
iter++ )
{
if((*iter).get()==pvRecordClient.get()) {
pvRecordClientList.erase(iter);
unlock();
return true;
}
}
unlock();
return false;
} catch (...) {
unlock();
throw;
}
}
void PVRecord::detachClients()
{
if(traceLevel>1) {
cout << "PVRecord::removePVRecordClient() " << recordName << endl;
}
lock();
try {
if(isDestroyed) {
unlock();
return;
}
std::list<PVRecordClientPtr>::iterator iter;
for (iter = pvRecordClientList.begin();
iter!=pvRecordClientList.end();
iter++ )
{
unlock();
(*iter)->detach(getPtrSelf());
lock();
}
pvRecordClientList.clear();
unlock();
} catch(...) {
unlock();
throw;
epicsGuard<epics::pvData::Mutex> guard(mutex);
if(isDestroyed) {
return false;
}
std::list<PVRecordClientPtr>::iterator iter;
for (iter = clientList.begin();
iter!=clientList.end();
iter++ )
{
PVRecordClientPtr client = *iter;
if(!client) continue;
if(client==pvRecordClient) {
clientList.erase(iter);
return true;
}
}
return false;
}
bool PVRecord::addListener(PVListenerPtr const & pvListener)
bool PVRecord::addListener(
PVListenerPtr const & pvListener,
PVCopyPtr const & pvCopy)
{
if(traceLevel>1) {
cout << "PVRecord::addListener() " << recordName << endl;
}
lock();
try {
if(isDestroyed) {
unlock();
epicsGuard<epics::pvData::Mutex> guard(mutex);
if(isDestroyed) {
return false;
}
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ )
{
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
if(listener.get()==pvListener.get()) {
return false;
}
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ )
{
if((*iter).get()==pvListener.get()) {
unlock();
return false;
}
}
pvListenerList.push_back(pvListener);
unlock();
return true;
} catch(...) {
unlock();
throw;
}
pvListenerList.push_back(pvListener);
this->pvListener = pvListener;
isAddListener = true;
pvCopy->traverseMaster(shared_from_this());
this->pvListener = PVListenerPtr();;
return true;
}
bool PVRecord::removeListener(PVListenerPtr const & pvListener)
void PVRecord::nextMasterPVField(PVFieldPtr const & pvField)
{
PVRecordFieldPtr pvRecordField = findPVRecordField(pvField);
PVListenerPtr listener = pvListener.lock();
if(!listener.get()) return;
if(isAddListener) {
pvRecordField->addListener(listener);
} else {
pvRecordField->removeListener(listener);
}
}
bool PVRecord::removeListener(
PVListenerPtr const & pvListener,
PVCopyPtr const & pvCopy)
{
if(traceLevel>1) {
cout << "PVRecord::removeListener() " << recordName << endl;
}
lock();
try {
if(isDestroyed) {
unlock();
return false;
}
std::list<PVListenerPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ )
{
if((*iter).get()==pvListener.get()) {
pvListenerList.erase(iter);
pvRecordStructure->removeListener(pvListener);
unlock();
return true;
}
}
unlock();
epicsGuard<epics::pvData::Mutex> guard(mutex);
if(isDestroyed) {
return false;
} catch(...) {
unlock();
throw;
}
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ )
{
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
if(listener.get()==pvListener.get()) {
pvListenerList.erase(iter);
this->pvListener = pvListener;
isAddListener = false;
pvCopy->traverseMaster(shared_from_this());
this->pvListener = PVListenerPtr();
return true;
}
}
return false;
}
void PVRecord::beginGroupPut()
@@ -388,10 +309,12 @@ void PVRecord::beginGroupPut()
if(traceLevel>2) {
cout << "PVRecord::beginGroupPut() " << recordName << endl;
}
std::list<PVListenerPtr>::iterator iter;
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++)
{
(*iter).get()->beginGroupPut(getPtrSelf());
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
listener->beginGroupPut(shared_from_this());
}
}
@@ -401,44 +324,23 @@ void PVRecord::endGroupPut()
if(traceLevel>2) {
cout << "PVRecord::endGroupPut() " << recordName << endl;
}
std::list<PVListenerPtr>::iterator iter;
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++)
{
(*iter).get()->endGroupPut(getPtrSelf());
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
listener->endGroupPut(shared_from_this());
}
}
void PVRecord::message(String const & message,MessageType messageType)
std::ostream& operator<<(std::ostream& o, const PVRecord& record)
{
if(isDestroyed) return;
if(requesterList.size()==0 ) {
String xxx(getMessageTypeName(messageType) + " " + message);
printf("%s\n",xxx.c_str());
return;
o << format::indent() << "record " << record.getRecordName() << endl;
{
format::indent_scope s(o);
o << *record.getPVRecordStructure()->getPVStructure();
}
std::list<epics::pvData::RequesterPtr>::iterator iter;
for(iter = requesterList.begin(); iter != requesterList.end(); ++iter) {
(*iter)->message(message,messageType);
}
}
void PVRecord::message(
PVRecordFieldPtr const & pvRecordField,
String const & message,
MessageType messageType)
{
this->message(pvRecordField->getFullName() + " " + message,messageType);
}
void PVRecord::toString(StringBuilder buf)
{
toString(buf,0);
}
void PVRecord::toString(StringBuilder buf,int indentLevel)
{
*buf += "\nrecord " + recordName + " ";
pvRecordStructure->getPVStructure()->toString(buf, indentLevel);
return o;
}
PVRecordField::PVRecordField(
@@ -454,51 +356,49 @@ PVRecordField::PVRecordField(
void PVRecordField::init()
{
fullFieldName = pvField->getFieldName();
PVRecordStructurePtr pvParent = parent;
while(pvParent.get()!= NULL) {
String parentName = pvParent->getPVField()->getFieldName();
fullFieldName = pvField.lock()->getFieldName();
PVRecordStructurePtr pvParent(parent.lock());
while(pvParent) {
string parentName = pvParent->getPVField()->getFieldName();
if(parentName.size()>0) {
fullFieldName = pvParent->getPVField()->getFieldName()
+ '.' + fullFieldName;
}
pvParent = pvParent->getParent();
}
PVRecordPtr pvRecord(this->pvRecord.lock());
if(fullFieldName.size()>0) {
fullName = pvRecord->getRecordName() + '.' + fullFieldName;
} else {
fullName = pvRecord->getRecordName();
}
pvField->setPostHandler(getPtrSelf());
pvField.lock()->setPostHandler(shared_from_this());
}
PVRecordField::~PVRecordField()
PVRecordStructurePtr PVRecordField::getParent()
{
return parent.lock();
}
void PVRecordField::destroy()
{
pvRecord.reset();
parent.reset();
pvField.reset();
pvListenerList.clear();
}
PVFieldPtr PVRecordField::getPVField() {return pvField.lock();}
PVRecordStructurePtr PVRecordField::getParent() {return parent;}
string PVRecordField::getFullFieldName() {return fullFieldName; }
PVFieldPtr PVRecordField::getPVField() {return pvField;}
string PVRecordField::getFullName() {return fullName; }
String PVRecordField::getFullFieldName() {return fullFieldName; }
String PVRecordField::getFullName() {return fullName; }
PVRecordPtr PVRecordField::getPVRecord() {return pvRecord;}
PVRecordPtr PVRecordField::getPVRecord() {return pvRecord.lock();}
bool PVRecordField::addListener(PVListenerPtr const & pvListener)
{
std::list<PVListenerPtr>::iterator iter;
PVRecordPtr pvRecord(this->pvRecord.lock());
if(pvRecord && pvRecord->getTraceLevel()>1) {
cout << "PVRecordField::addListener() " << getFullName() << endl;
}
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) {
if((*iter).get()==pvListener.get()) {
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
if(listener.get()==pvListener.get()) {
return false;
}
}
@@ -508,9 +408,15 @@ bool PVRecordField::addListener(PVListenerPtr const & pvListener)
void PVRecordField::removeListener(PVListenerPtr const & pvListener)
{
std::list<PVListenerPtr>::iterator iter;
PVRecordPtr pvRecord(this->pvRecord.lock());
if(pvRecord && pvRecord->getTraceLevel()>1) {
cout << "PVRecordField::removeListener() " << getFullName() << endl;
}
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) {
if((*iter).get()==pvListener.get()) {
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
if(listener.get()==pvListener.get()) {
pvListenerList.erase(iter);
return;
}
@@ -519,28 +425,32 @@ void PVRecordField::removeListener(PVListenerPtr const & pvListener)
void PVRecordField::postPut()
{
if(parent!=NULL) {
parent->postParent(getPtrSelf());
PVRecordStructurePtr parent(this->parent.lock());;
if(parent) {
parent->postParent(shared_from_this());
}
postSubField();
}
void PVRecordField::postParent(PVRecordFieldPtr const & subField)
{
PVRecordStructurePtr pvrs = static_pointer_cast<PVRecordStructure>(getPtrSelf());
std::list<PVListenerPtr>::iterator iter;
PVRecordStructurePtr pvrs = static_pointer_cast<PVRecordStructure>(shared_from_this());
std::list<PVListenerWPtr>::iterator iter;
for(iter = pvListenerList.begin(); iter != pvListenerList.end(); ++iter)
{
(*iter)->dataPut(pvrs,subField);
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
listener->dataPut(pvrs,subField);
}
if(parent!=NULL) parent->postParent(subField);
PVRecordStructurePtr parent(this->parent.lock());
if(parent) parent->postParent(subField);
}
void PVRecordField::postSubField()
{
callListener();
if(isStructure) {
PVRecordStructurePtr pvrs = static_pointer_cast<PVRecordStructure>(getPtrSelf());
PVRecordStructurePtr pvrs = static_pointer_cast<PVRecordStructure>(shared_from_this());
PVRecordFieldPtrArrayPtr pvRecordFields = pvrs->getPVRecordFields();
PVRecordFieldPtrArray::iterator iter;
for(iter = pvRecordFields->begin() ; iter !=pvRecordFields->end(); iter++) {
@@ -549,16 +459,13 @@ void PVRecordField::postSubField()
}
}
void PVRecordField::message(String const & message,MessageType messageType)
{
pvRecord->message(getPtrSelf(),message,messageType);
}
void PVRecordField::callListener()
{
std::list<PVListenerPtr>::iterator iter;
std::list<PVListenerWPtr>::iterator iter;
for (iter = pvListenerList.begin(); iter!=pvListenerList.end(); iter++ ) {
(*iter)->dataPut(getPtrSelf());
PVListenerPtr listener = iter->lock();
if(!listener.get()) continue;
listener->dataPut(shared_from_this());
}
}
@@ -573,30 +480,14 @@ PVRecordStructure::PVRecordStructure(
{
}
PVRecordStructure::~PVRecordStructure()
{
}
void PVRecordStructure::destroy()
{
PVRecordFieldPtrArray::iterator iter;
PVRecordField::destroy();
for(iter = pvRecordFields->begin() ; iter !=pvRecordFields->end(); iter++) {
(*iter)->destroy();
}
PVRecordField::destroy();
pvRecordFields.reset();
pvStructure.reset();
}
void PVRecordStructure::init()
{
PVRecordField::init();
const PVFieldPtrArray & pvFields = pvStructure->getPVFields();
const PVFieldPtrArray & pvFields = pvStructure.lock()->getPVFields();
size_t numFields = pvFields.size();
pvRecordFields->reserve( numFields);
PVRecordStructurePtr self =
static_pointer_cast<PVRecordStructure>(getPtrSelf());
static_pointer_cast<PVRecordStructure>(shared_from_this());
PVRecordPtr pvRecord = getPVRecord();
for(size_t i=0; i<numFields; i++) {
PVFieldPtr pvField = pvFields[i];
@@ -620,26 +511,6 @@ PVRecordFieldPtrArrayPtr PVRecordStructure::getPVRecordFields()
return pvRecordFields;
}
PVStructurePtr PVRecordStructure::getPVStructure() {return pvStructure;}
void PVRecordStructure::removeListener(PVListenerPtr const & pvListener)
{
PVRecordField::removeListener(pvListener);
size_t numFields = pvRecordFields->size();
for(size_t i=0; i<numFields; i++) {
PVRecordFieldPtr pvRecordField = (*pvRecordFields.get())[i];
pvRecordField->removeListener(pvListener);
}
}
void PVRecordStructure::postPut()
{
PVRecordField::postPut();
size_t numFields = pvRecordFields->size();
for(size_t i=0; i<numFields; i++) {
PVRecordFieldPtr pvRecordField = (*pvRecordFields.get())[i];
pvRecordField->callListener();
}
}
PVStructurePtr PVRecordStructure::getPVStructure() {return pvStructure.lock();}
}}

View File

@@ -0,0 +1,412 @@
/* channelProviderLocal.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author Marty Kraimer
* @date 2013.04
*/
#ifndef CHANNELPROVIDERLOCAL_H
#define CHANNELPROVIDERLOCAL_H
#ifdef epicsExportSharedSymbols
# define channelProviderLocalEpicsExportSharedSymbols
# undef epicsExportSharedSymbols
#endif
#include <string>
#include <cstring>
#include <stdexcept>
#include <memory>
#include <set>
#include <pv/lock.h>
#include <pv/pvType.h>
#include <pv/pvData.h>
#include <pv/pvCopy.h>
#include <pv/pvAccess.h>
#include <pv/status.h>
#include <pv/serverContext.h>
#ifdef channelProviderLocalEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef channelProviderLocalEpicsExportSharedSymbols
#endif
#include <shareLib.h>
#include <pv/pvDatabase.h>
namespace epics { namespace pvDatabase {
class MonitorFactory;
typedef std::tr1::shared_ptr<MonitorFactory> MonitorFactoryPtr;
class MonitorLocal;
typedef std::tr1::shared_ptr<MonitorLocal> MonitorLocalPtr;
class ChannelProviderLocal;
typedef std::tr1::shared_ptr<ChannelProviderLocal> ChannelProviderLocalPtr;
class ChannelLocal;
typedef std::tr1::shared_ptr<ChannelLocal> ChannelLocalPtr;
epicsShareFunc MonitorFactoryPtr getMonitorFactory();
/**
* @brief MonitorFactory
*
* This class provides a static method to create a monitor for a PVRecord
*/
class epicsShareClass MonitorFactory
{
public:
POINTER_DEFINITIONS(MonitorFactory);
/**
* @brief Destructor
*/
virtual ~MonitorFactory();
/**
* @brief Destroy the monitor factory.
*/
virtual void destroy();
/**
* @brief Create a monitor on a record.
*
* This is called by the local channel provider.
* @param pvRecord The record to monitor.
* @param monitorRequester The client callback.
* @param pvRequest Options specified by the client.
* This includes the subset of the fields in the record to monitor.
* @return A shared pointer to the newly created monitor.
* If the monitor can not be created a null monitor is returned.
* This means the pvRequest specified options that could not be satisfied.
*/
epics::pvData::MonitorPtr createMonitor(
PVRecordPtr const & pvRecord,
epics::pvData::MonitorRequester::shared_pointer const & monitorRequester,
epics::pvData::PVStructurePtr const & pvRequest);
private:
MonitorFactory();
friend class MonitorLocal;
friend epicsShareFunc MonitorFactoryPtr getMonitorFactory();
bool isDestroyed;
epics::pvData::Mutex mutex;
};
epicsShareFunc ChannelProviderLocalPtr getChannelProviderLocal();
/**
* @brief ChannelProvider for PVDatabase.
*
* An implementation of channelProvider that provides access to records in PVDatabase.
*/
class epicsShareClass ChannelProviderLocal :
public epics::pvAccess::ChannelProvider,
public std::tr1::enable_shared_from_this<ChannelProviderLocal>
{
public:
POINTER_DEFINITIONS(ChannelProviderLocal);
/**
* @brief Destructor
*/
virtual ~ChannelProviderLocal();
/**
* @brief Destroy the channel provider.
*
* Probably never called.
*/
virtual void destroy();
/**
* @brief Returns the channel provider name.
* @return <b>local</b>
*/
virtual std::string getProviderName();
/**
* @brief Returns either a null channelFind or a channelFind for records in the PVDatabase.
* @param channelName The name of the channel desired.
* @param channelFindRequester The client callback.
* @return shared pointer to ChannelFind.
* This is null if the channelName is not the name of a record in the PVDatabase.
* It is an implementation of SyncChannelFind if the channelName is the name
* of a record in the PVDatabase.
* The interface for SyncChannelFind is defined by pvAccessCPP.
* The channelFindResult method of channelFindRequester is called before the
* method returns.
*/
virtual epics::pvAccess::ChannelFind::shared_pointer channelFind(
std::string const &channelName,
epics::pvAccess::ChannelFindRequester::shared_pointer const & channelFindRequester);
/**
* @brief Calls method channelListRequester::channelListResult.
*
* This provides the caller with a list of the record names on the PVDatabase.
* A record name is the same as a channel name.
* @param channelListRequester The client callback.
* @return shared pointer to ChannelFind.
* The interface for SyncChannelFind is defined by pvAccessCPP.
*/
virtual epics::pvAccess::ChannelFind::shared_pointer channelList(
epics::pvAccess::ChannelListRequester::shared_pointer const & channelListRequester);
/**
* @brief Create a channel for a record.
*
* This method just calls the next method with a address of "".
* @param channelName The name of the channel desired.
* @param channelRequester The client callback.
* @param priority The priority.
* @return shared pointer to Channel.
*/
virtual epics::pvAccess::Channel::shared_pointer createChannel(
std::string const &channelName,
epics::pvAccess::ChannelRequester::shared_pointer const &channelRequester,
short priority);
/**
* @brief Create a channel for a record.
* @param channelName The name of the channel desired.
* @param channelRequester The callback to call with the result.
* @param priority The priority.
* This is ignored.
* @param address The address.
* This is ignored.
* @return shared pointer to Channel.
* This is null if the channelName is not the name of a record in the PVDatabase.
* Otherwise it is a newly created channel inteface.
* ChannelRequester::channelCreated is called to give the result.
*/
virtual epics::pvAccess::Channel::shared_pointer createChannel(
std::string const &channelName,
epics::pvAccess::ChannelRequester::shared_pointer const &channelRequester,
short priority,
std::string const &address);
private:
shared_pointer getPtrSelf()
{
return shared_from_this();
}
ChannelProviderLocal();
friend epicsShareFunc ChannelProviderLocalPtr getChannelProviderLocal();
PVDatabasePtr pvDatabase;
epics::pvData::Mutex mutex;
bool beingDestroyed;
epics::pvAccess::ChannelFind::shared_pointer channelFinder;
friend class ChannelProviderLocalRun;
};
/**
* @brief Channel for accessing a PVRecord.
*
* A Channel for accessing a record in the PVDatabase.
* It is a complete implementation of Channel
*/
class epicsShareClass ChannelLocal :
public epics::pvAccess::Channel,
public PVRecordClient,
public std::tr1::enable_shared_from_this<ChannelLocal>
{
public:
POINTER_DEFINITIONS(ChannelLocal);
/** Constructor
* @param channelProvider The channel provider.
* @param requester The client callback.
* @param pvRecord The record the channel will access.
*/
ChannelLocal(
ChannelProviderLocalPtr const &channelProvider,
epics::pvAccess::ChannelRequester::shared_pointer const & requester,
PVRecordPtr const & pvRecord
);
/**
* @brief Destructor
*/
virtual ~ChannelLocal();
/**
* @brief Destroy the channel.
*
* It cleans up all resources used to access the record.
* Note that this assumes that client has destroyed any objects that
* have been created for the channel like channelGet, etc.
* The remote pvAccess server does this cleanup.
*/
virtual void destroy();
/**
* @brief Detach from the record.
*
* This is called when a record is being removed from the database.
* Calls destroy.
* @param pvRecord The record being destroyed.
*/
virtual void detach(PVRecordPtr const &pvRecord);
/**
* @brief Get the requester name.
* @return returns the name of the channel requester.
*/
virtual std::string getRequesterName();
/**
* @brief Passes the message to the channel requester.
* @param message The message.
* @param messageType The message type.
*/
virtual void message(
std::string const & message,
epics::pvData::MessageType messageType);
/**
* @brief Get the channel provider
* @return The provider.
*/
virtual epics::pvAccess::ChannelProvider::shared_pointer getProvider()
{
return provider;
}
/**
* @brief Get the remote address
* @return <b>local</b>
*/
virtual std::string getRemoteAddress();
/**
* Get the connection state.
* @return Channel::CONNECTED.
*/
virtual epics::pvAccess::Channel::ConnectionState getConnectionState();
/**
* @brief Get the channel name.
* @return the record name.
*/
virtual std::string getChannelName();
/**
* @brief Get the channel requester
* @return The channel requester.
*/
virtual epics::pvAccess::ChannelRequester::shared_pointer getChannelRequester();
/**
* @brief Is the channel connected?
* @return true.
*/
virtual bool isConnected();
/**
* @brief Get the introspection interface for subField.
*
* The introspection interface is given via GetFieldRequester::getDone.
* @param requester The client callback.
* @param subField The subField of the record.
* If an empty string then the interface for the top level structure of
* the record is provided.
*/
virtual void getField(
epics::pvAccess::GetFieldRequester::shared_pointer const &requester,
std::string const & subField);
/**
* Get the access rights for the record.
* This throws an exception because it is assumed that access rights are
* handled by a higher level.
*/
virtual epics::pvAccess::AccessRights getAccessRights(
epics::pvData::PVField::shared_pointer const &pvField);
/**
* @brief Create a channelProcess.
*
* @param requester The client callback.
* @param pvRequest The options specified by the client.
* @return A shared pointer to the newly created implementation.
* The implementation is null if pvRequest has invalid options.
*/
virtual epics::pvAccess::ChannelProcess::shared_pointer createChannelProcess(
epics::pvAccess::ChannelProcessRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
* @brief Create a channelGet.
*
* @param requester The client callback.
* @param pvRequest The options specified by the client.
* @return A shared pointer to the newly created implementation.
* The implementation is null if pvRequest has invalid options.
*/
virtual epics::pvAccess::ChannelGet::shared_pointer createChannelGet(
epics::pvAccess::ChannelGetRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
* @brief Create a channelPut.
*
* @param requester The client callback.
* @param pvRequest The options specified by the client.
* @return A shared pointer to the newly created implementation.
* The implementation is null if pvRequest has invalid options.
*/
virtual epics::pvAccess::ChannelPut::shared_pointer createChannelPut(
epics::pvAccess::ChannelPutRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
* @brief Create a channelPutGet.
*
* @param requester The client callback.
* @param pvRequest The options specified by the client.
* @return A shared pointer to the newly created implementation.
* The implementation is null if pvRequest has invalid options.
*/
virtual epics::pvAccess::ChannelPutGet::shared_pointer createChannelPutGet(
epics::pvAccess::ChannelPutGetRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
* @brief Create a channelRPC.
*
* The PVRecord must implement <b>getService</b> or an empty shared pointer is returned.
* @param requester The client callback
* @param pvRequest The options specified by the client.
* @return null.
*/
virtual epics::pvAccess::ChannelRPC::shared_pointer createChannelRPC(
epics::pvAccess::ChannelRPCRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
* @brief Create a monitor.
*
* @param requester The client callback.
* @param pvRequest The options specified by the client.
* @return A shared pointer to the newly created implementation.
* The implementation is null if pvRequest has invalid options.
*/
virtual epics::pvData::Monitor::shared_pointer createMonitor(
epics::pvData::MonitorRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
* @brief Create a channelArray.
*
* @param requester The client callback.
* @param pvRequest The options specified by the client.
* @return A shared pointer to the newly created implementation.
* The implementation is null if pvRequest has invalid options.
*/
virtual epics::pvAccess::ChannelArray::shared_pointer createChannelArray(
epics::pvAccess::ChannelArrayRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
/**
* @brief calls printInfo(std::cout);
*/
virtual void printInfo();
/**
* @brief displays a message
*
* @param out the stream on which the message is displayed.
*/
virtual void printInfo(std::ostream& out);
protected:
shared_pointer getPtrSelf()
{
return shared_from_this();
}
private:
epics::pvAccess::ChannelRequester::shared_pointer requester;
ChannelProviderLocalPtr provider;
PVRecordPtr pvRecord;
bool isDestroyed;
epics::pvData::Mutex mutex;
};
}}
#endif /* CHANNELPROVIDERLOCAL_H */

View File

@@ -1,28 +1,39 @@
/* pvDatabase.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2012.11.20
*/
#ifndef PVDATABASE_H
#define PVDATABASE_H
#ifdef epicsExportSharedSymbols
# define pvdatabaseEpicsExportSharedSymbols
# undef epicsExportSharedSymbols
#endif
#include <list>
#include <map>
#include <deque>
#include <pv/pvData.h>
#include <pv/convert.h>
#include <pv/pvCopy.h>
#include <pv/pvTimeStamp.h>
#include <pv/rpcService.h>
#ifdef pvdatabaseEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef pvdatabaseEpicsExportSharedSymbols
#endif
#include <shareLib.h>
namespace epics { namespace pvDatabase {
class PVRecord;
typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
typedef std::tr1::weak_ptr<PVRecord> PVRecordWPtr;
typedef std::map<std::string,PVRecordPtr> PVRecordMap;
class PVRecordField;
typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
@@ -31,105 +42,133 @@ typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
class PVRecordStructure;
typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
typedef std::tr1::weak_ptr<PVRecordStructure> PVRecordStructureWPtr;
class PVRecordClient;
typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
class PVListener;
typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
typedef std::tr1::weak_ptr<PVListener> PVListenerWPtr;
class PVDatabase;
typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
/**
* Base interface for a record.
* @brief Base interface for a PVRecord.
*
* It is also a complete implementation for <b>soft</b> records.
* A soft record is a record where method <b>process</b> sets an
* optional top level timeStamp field to the current time and does nothing else.
* @author mrk
* @date 2012.11.20
*/
class PVRecord :
public virtual epics::pvData::Requester,
class epicsShareClass PVRecord :
public epics::pvData::PVCopyTraverseMasterCallback,
public std::tr1::enable_shared_from_this<PVRecord>
{
public:
POINTER_DEFINITIONS(PVRecord);
/**
* Virtual initialization method.
* Must be implemented by derived classes.
* This method <b>Must</b> call initPVRecord.
* The Destructor.
*/
virtual ~PVRecord();
/**
* @brief Optional initialization method.
*
* A derived method <b>Must</b> call initPVRecord.
* @return <b>true</b> for success and <b>false</b> for failure.
*/
virtual bool init() {initPVRecord(); return true;}
/**
* Optional method for derived class.
* @brief Optional method for derived class.
*
* It is called before record is added to database.
*/
virtual void start() {}
/**
* Must be implemented by derived classes.
* @brief Optional method for derived class.
*
* It is the method that makes a record smart.
* If it encounters errors it should raise alarms and/or
* call the <b>message</b> method provided by the base class.
* If the pvStructure has a top level timeStamp,
* the base class sets the timeStamp to the current time.
*/
virtual void process() {}
virtual void process();
/**
* Destroy the PVRecord. Release any resources used and
* @brief Optional method for derived class.
*
* Destroy the PVRecord. Release any resources used and
* get rid of listeners and requesters.
* If derived class overrides this then it must call PVRecord::destroy()
* after it has destroyed rewsorces it uses.
* after it has destroyed any resorces it uses.
*/
virtual void destroy();
/**
* Creates a <b>dump</b> record, i.e. a record where process does nothing.
* @brief Optional method for derived class.
*
* Return a service corresponding to the specified request PVStructure.
* @param pvRequest The request PVStructure
* @return The corresponding service
*/
virtual epics::pvAccess::Service::shared_pointer getService(
epics::pvData::PVStructurePtr const & pvRequest)
{
return epics::pvAccess::Service::shared_pointer();
}
/**
* @brief Creates a <b>soft</b> record.
*
* @param recordName The name of the record, which is also the channelName.
* @param pvStructure The top level structure.
* @return A shared pointer to the newly created record.
*/
static PVRecordPtr create(
epics::pvData::String const & recordName,
std::string const & recordName,
epics::pvData::PVStructurePtr const & pvStructure);
/**
* The Destructor. Must be virtual.
*/
virtual ~PVRecord();
/**
* Get the name of the record.
* @brief Get the name of the record.
*
* @return The name.
*/
epics::pvData::String getRecordName();
std::string getRecordName() const { return recordName;}
/**
* Get the top level PVStructure.
* @brief Get the top level PVRecordStructure.
*
* @return The shared pointer.
*/
PVRecordStructurePtr getPVRecordStructure();
PVRecordStructurePtr getPVRecordStructure() const { return pvRecordStructure;}
/**
* Find the PVRecordField for the PVField.
* @brief Get the top level PVStructure.
*
* @return The top level PVStructure.
*/
epics::pvData::PVStructurePtr getPVStructure() const { return pvStructure;}
/**
* @brief Find the PVRecordField for the PVField.
*
* This is called by the pvCopy facility.
* @param pvField The PVField.
* @return The shared pointer to the PVRecordField.
*/
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const & pvField);
/**
* Add a requester to receive messages.
* @param requester The requester.
* @return <b>true</b> if requester was added.
*/
bool addRequester(epics::pvData::RequesterPtr const & requester);
/**
* Remove a requester.
* @param requester The requester.`
* @return <b>true</b> if requester was removed.
*/
bool removeRequester(epics::pvData::RequesterPtr const & requester);
/**
* Lock the record.
* @brief Lock the record.
*
* Any code must lock while accessing a record.
*/
void lock();
/**
* Unlock the record.
* @brief Unlock the record.
*
* The code that calls lock must unlock when done accessing the record.
*/
void unlock();
/**
* @brief Try to lock the record.
*
* If <b>true</b> then just like <b>lock</b>.
* If <b>false</b>client can not access record.
* Code can try to simultaneously hold the lock for more than two records
@@ -138,6 +177,8 @@ public:
*/
bool tryLock();
/**
* @brief Lock another record.
*
* A client that holds the lock for one record can lock one other record.
* A client <b>must</b> not call this if the client already has the lock for
* more then one record.
@@ -146,6 +187,8 @@ public:
*/
void lockOtherRecord(PVRecordPtr const & otherRecord);
/**
* @brief Add a client that wants to access the record.
*
* Every client that accesses the record must call this so that the
* client can be notified when the record is deleted.
* @param pvRecordClient The client.
@@ -153,100 +196,78 @@ public:
*/
bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
/**
* Remove a client.
* @brief Remove a client.
*
* @param pvRecordClient The client.
* @return <b>true</b> if the client is removed.
*/
bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
/**
* remove all attached clients.
*/
void detachClients();
/**
* Add a PVListener.
* @brief Add a PVListener.
*
* This must be called before calling pvRecordField.addListener.
* @param pvListener The listener.
* @param pvCopy The pvStructure that has the client fields.
* @return <b>true</b> if the listener was added.
*/
bool addListener(PVListenerPtr const & pvListener);
bool addListener(
PVListenerPtr const & pvListener,
epics::pvData::PVCopyPtr const & pvCopy);
/**
* Remove a listener.
* @brief PVCopyTraverseMasterCallback method
*
* @param pvField The next client field.
*/
void nextMasterPVField(epics::pvData::PVFieldPtr const & pvField);
/**
* @brief Remove a listener.
*
* @param pvListener The listener.
* @param pvCopy The pvStructure that has the client fields.
* @return <b>true</b> if the listener was removed.
*/
bool removeListener(PVListenerPtr const & pvListener);
bool removeListener(
PVListenerPtr const & pvListener,
epics::pvData::PVCopyPtr const & pvCopy);
/**
* Begins a group of puts.
* @brief Begins a group of puts.
*/
void beginGroupPut();
/**
* Ends a group of puts.
* @brief Ends a group of puts.
*/
void endGroupPut();
/**
* Virtual method of <b>Requester</b>
* @return the name of the requester.
*/
epics::pvData::String getRequesterName() {return getRecordName();}
/**
* Can be called by implementation code.
* The message will be sent to every requester.
* @param message The message.
* @param messageType The severity of the message.
*/
virtual void message(
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
/**
* Called for a field. It will call the previous method
* after adding field name.
* @param pvRecordField The field for which the message is associated.
* @param message The message.
* @param messageType The severity of the message.
*/
void message(
PVRecordFieldPtr const & pvRecordField,
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
/**
* Calls the next method with indentLevel = 0.
* @param buf String Builder.
*/
void toString(epics::pvData::StringBuilder buf);
/**
* Dumps the data from the top level PVStructure.
* @param buf String Builder.
* @param indentLevel The indentation level.
*/
void toString(epics::pvData::StringBuilder buf,int indentLevel);
/**
* get trace level (0,1,2) means (nothing,lifetime,process)
* @brief get trace level (0,1,2) means (nothing,lifetime,process)
* @return the level
*/
int getTraceLevel() {return traceLevel;}
/**
* set trace level (0,1) means (lifetime,process)
* @brief set trace level (0,1,2) means (nothing,lifetime,process)
* @param level The level
*/
void setTraceLevel(int level) {traceLevel = level;}
protected:
/**
* Constructor
* @brief Constructor
* @param recordName The name of the record
* @param pvStructure The top level PVStructutre
*/
PVRecord(
epics::pvData::String const & recordName,
std::string const & recordName,
epics::pvData::PVStructurePtr const & pvStructure);
/**
* Initializes the base class. Must be called by derived classes.
* @brief Initializes the base class.
*
* Must be called by derived classes.
*/
void initPVRecord();
/**
* Convience method for derived classes.
* @return The shared pointer to the top level PVStructure.
/**
* @brief Get shared pointer to self.
* @return The shared pointer.
*/
epics::pvData::PVStructurePtr getPVStructure();
PVRecordPtr getPtrSelf()
{
return shared_from_this();
@@ -255,134 +276,117 @@ private:
PVRecordFieldPtr findPVRecordField(
PVRecordStructurePtr const & pvrs,
epics::pvData::PVFieldPtr const & pvField);
epics::pvData::String recordName;
std::string recordName;
epics::pvData::PVStructurePtr pvStructure;
epics::pvData::ConvertPtr convert;
PVRecordStructurePtr pvRecordStructure;
std::list<PVListenerPtr> pvListenerList;
std::list<PVRecordClientPtr> pvRecordClientList;
std::list<epics::pvData::RequesterPtr> requesterList;
std::list<PVListenerWPtr> pvListenerList;
std::list<PVRecordClientPtr> clientList;
epics::pvData::Mutex mutex;
std::size_t depthGroupPut;
int traceLevel;
bool isDestroyed;
epics::pvData::PVTimeStamp pvTimeStamp;
epics::pvData::TimeStamp timeStamp;
// following only valid while addListener or removeListener is active.
bool isAddListener;
PVListenerWPtr pvListener;
};
epicsShareFunc std::ostream& operator<<(std::ostream& o, const PVRecord& record);
/**
* Interface for a field of a record.
* @brief Interface for a field of a record.
*
* One exists for each field of the top level PVStructure.
* @author mrk
*/
class PVRecordField :
class epicsShareClass PVRecordField :
public virtual epics::pvData::PostHandler,
public std::tr1::enable_shared_from_this<PVRecordField>
{
public:
POINTER_DEFINITIONS(PVRecordField);
/**
* Constructor.
* @brief Constructor.
*
* @param pvField The field from the top level structure.
* @param The parent.
* @param parent The parent.
* @param pvRecord The PVRecord.
*/
PVRecordField(
epics::pvData::PVFieldPtr const & pvField,
PVRecordStructurePtr const &parent,
PVRecordPtr const & pvRecord);
/**
* Destructor.
* @brief Destructor.
*/
virtual ~PVRecordField();
virtual ~PVRecordField() {}
/**
* Release any resources used
*/
virtual void destroy();
/**
* Get the parent.
* @brief Get the parent.
*
* @return The parent.
*/
PVRecordStructurePtr getParent();
/**
* Get the PVField.
* @brief Get the PVField.
*
* @return The shared pointer.
*/
epics::pvData::PVFieldPtr getPVField();
/**
* Get the full name of the field, i.e. field,field,..
* @brief Get the full name of the field, i.e. field,field,..
* @return The full name.
*/
epics::pvData::String getFullFieldName();
std::string getFullFieldName();
/**
* Get the recordName plus the full name of the field, i.e. recordName.field,field,..
* @brief Get the recordName plus the full name of the field, i.e. recordName.field,field,..
* @return The name.
*/
epics::pvData::String getFullName();
std::string getFullName();
/**
* Returns the PVRecord to which this field belongs.
* @brief Return the PVRecord to which this field belongs.
* @return The shared pointer,
*/
PVRecordPtr getPVRecord();
/**
* Add A PVListener to this field.
* Whenever this field or any subfield if this field is modified the listener will be notified.
* PVListener is described below.
* Before a listener can call addListener it must first call PVRecord.registerListener.
* @param pvListener The listener.
* @return <b<true</b> if listener is added.
*/
bool addListener(PVListenerPtr const & pvListener);
/**
* Remove a listener.
* @param pvListener The listener.
* @return <b<true</b> if listener is removed.
*/
virtual void removeListener(PVListenerPtr const & pvListener);
/**
* This is called by the code that implements the data interface.
* @brief This is called by the code that implements the data interface.
* It is called whenever the put method is called.
*/
virtual void postPut();
/**
* Called by implementation code.
* It calls PVRecord::message after prepending the full fieldname.
* @param message The message,
* @param messageType The message severity.
* @return
*/
virtual void message(
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
protected:
PVRecordFieldPtr getPtrSelf()
{
return shared_from_this();
}
virtual void init();
virtual void postParent(PVRecordFieldPtr const & subField);
virtual void postSubField();
private:
bool addListener(PVListenerPtr const & pvListener);
virtual void removeListener(PVListenerPtr const & pvListener);
void callListener();
std::list<PVListenerPtr> pvListenerList;
epics::pvData::PVFieldPtr pvField;
std::list<PVListenerWPtr> pvListenerList;
epics::pvData::PVField::weak_pointer pvField;
bool isStructure;
PVRecordStructurePtr parent;
PVRecordPtr pvRecord;
epics::pvData::String fullName;
epics::pvData::String fullFieldName;
PVRecordStructureWPtr parent;
PVRecordWPtr pvRecord;
std::string fullName;
std::string fullFieldName;
friend class PVRecordStructure;
friend class PVRecord;
};
/**
* Interface for a field that is a structure.
* @brief Interface for a field that is a structure.
*
* One exists for each structure field of the top level PVStructure.
* @author mrk
*/
class PVRecordStructure : public PVRecordField {
class epicsShareClass PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
/**
* Constructor.
* @brief Constructor.
* @param pvStructure The data.
* @param parent The parent
* @param pvRecord The record that has this field.
@@ -392,82 +396,74 @@ public:
PVRecordStructurePtr const &parent,
PVRecordPtr const & pvRecord);
/**
* Destructor.
* @brief Destructor.
*/
virtual ~PVRecordStructure();
virtual ~PVRecordStructure() {}
/**
* Release any resources used
*/
virtual void destroy();
/**
* Get the sub fields.
* @brief Get the sub fields.
* @return the array of PVRecordFieldPtr.
*/
PVRecordFieldPtrArrayPtr getPVRecordFields();
/**
* Get the data structure/
* @brief Get the data structure/
* @return The shared pointer.
*/
epics::pvData::PVStructurePtr getPVStructure();
/**
* Called by PVRecord::removeListener.
* @param pvListener The listener.
*/
virtual void removeListener(PVListenerPtr const & pvListener);
/**
* Called by implementation code of PVRecord.
*/
virtual void postPut();
protected:
/**
* Called by implementation code of PVRecord.
* @brief Called by implementation code of PVRecord.
*/
virtual void init();
private:
epics::pvData::PVStructurePtr pvStructure;
epics::pvData::PVStructure::weak_pointer pvStructure;
PVRecordFieldPtrArrayPtr pvRecordFields;
friend class PVRecord;
};
/**
* An interface that must be implemented by any code that accesses the record.
* @brief An interface implemented by code that accesses the record.
*
* @author mrk
*/
class PVRecordClient {
class epicsShareClass PVRecordClient {
public:
POINTER_DEFINITIONS(PVRecordClient);
/**
* Destructor.
* @brief Destructor.
*/
virtual ~PVRecordClient() {}
/**
* Detach from the record because it is being removed.
* @brief Detach from the record because it is being removed.
* @param pvRecord The record.
*/
virtual void detach(PVRecordPtr const & pvRecord) = 0;
};
/**
* @brief Listener for PVRecord::message.
*
* An interface that is implemented by code that traps calls to PVRecord::message.
* @author mrk
*/
class PVListener :
class epicsShareClass PVListener :
virtual public PVRecordClient
{
public:
POINTER_DEFINITIONS(PVListener);
/**
* Destructor.
* @brief Destructor.
*/
virtual ~PVListener() {}
/**
* pvField has been modified.
* @brief pvField has been modified.
*
* This is called if the listener has called PVRecordField::addListener for pvRecordField.
* @param pvRecordField The modified field.
*/
virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
/**
* A subfield has been modified.
* @brief A subfield has been modified.
*
* @param requested The structure that was requested.
* @param pvRecordField The field that was modified.
*/
@@ -475,40 +471,43 @@ public:
PVRecordStructurePtr const & requested,
PVRecordFieldPtr const & pvRecordField) = 0;
/**
* Begin a set of puts.
* @brief Begin a set of puts.
* @param pvRecord The record.
*/
virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
/**
* End a set of puts.
* @brief End a set of puts.
* @param pvRecord The record.
*/
virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
/**
* Connection to record is being terminated.
* @brief Connection to record is being terminated.
* @param pvRecord The record.
*/
virtual void unlisten(PVRecordPtr const & pvRecord) = 0;
};
/**
* The interface to a database of PVRecords.
* @brief The interface for a database of PVRecords.
*
* @author mrk
*/
class PVDatabase : virtual public epics::pvData::Requester {
class epicsShareClass PVDatabase {
public:
POINTER_DEFINITIONS(PVDatabase);
/**
* Get the master database.
* @brief Get the master database.
* @return The shared pointer.
*/
static PVDatabasePtr getMaster();
/**
* Destructor
* @brief Destructor
*/
virtual ~PVDatabase();
/**
* Destroy the PVDatabase.
* @brief Destroy the PVDatabase.
*
* For each record in the database the record is removed and it's destroy method is called.
*/
virtual void destroy();
/**
@@ -517,38 +516,25 @@ public:
* @param recordName The record to find.
* @return The shared pointer.
*/
PVRecordPtr findRecord(epics::pvData::String const& recordName);
PVRecordPtr findRecord(std::string const& recordName);
/**
* Add a record.
* @param The record to add.
* @brief Add a record.
*
* @param record The record to add.
* @return <b>true</b> if record was added.
*/
bool addRecord(PVRecordPtr const & record);
/**
* Remove a record.
* @param The record to remove.
* @brief Remove a record.
* @param record The record to remove.
* @return <b>true</b> if record was removed.
*/
bool removeRecord(PVRecordPtr const & record);
/**
* Get the names of all the records in the database.
* @brief Get the names of all the records in the database.
* @return The names.
*/
epics::pvData::PVStringArrayPtr getRecordNames();
/**
* Virtual method of Requester.
* @return The name.
*/
virtual epics::pvData::String getRequesterName();
/**
* Virtual method of Requester.
* @param message The message.
* @param messageType The message severity.
* @return
*/
virtual void message(
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
private:
PVDatabase();
void lock();
@@ -556,9 +542,9 @@ private:
PVRecordMap recordMap;
epics::pvData::Mutex mutex;
bool isDestroyed;
};
}}
#endif /* PVDATABASE_H */

64
src/pv/removeRecord.h Normal file
View File

@@ -0,0 +1,64 @@
/* removeRecord.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author mrk
* @date 2013.04.18
*/
#ifndef REMOVERECORD_H
#define REMOVERECORD_H
#include <shareLib.h>
#include <pv/channelProviderLocal.h>
namespace epics { namespace pvDatabase {
class RemoveRecord;
typedef std::tr1::shared_ptr<RemoveRecord> RemoveRecordPtr;
/**
* @brief Remove another record in the same database.
*
* A record to remove another record
* It is meant to be used via a channelPutGet request.
* The argument has one field: recordName.
* The result has a field named status.
*/
class epicsShareClass RemoveRecord :
public PVRecord
{
public:
POINTER_DEFINITIONS(RemoveRecord);
/**
* Factory methods to create RemoveRecord.
* @param recordName The name for the RemoveRecord.
* @return A shared pointer to RemoveRecord..
*/
static RemoveRecordPtr create(
std::string const & recordName);
/**
* standard init method required by PVRecord
* @return true unless record name already exists.
*/
virtual bool init();
/**
* @brief Remove the record specified by recordName.
*/
virtual void process();
private:
RemoveRecord(
std::string const & recordName,
epics::pvData::PVStructurePtr const & pvStructure);
PVDatabasePtr pvDatabase;
epics::pvData::PVStringPtr pvRecordName;
epics::pvData::PVStringPtr pvResult;
};
}}
#endif /* REMOVERECORD_H */

View File

@@ -11,6 +11,8 @@
#ifndef TRACERECORD_H
#define TRACERECORD_H
#include <shareLib.h>
#include <pv/channelProviderLocal.h>
namespace epics { namespace pvDatabase {
@@ -19,26 +21,44 @@ namespace epics { namespace pvDatabase {
class TraceRecord;
typedef std::tr1::shared_ptr<TraceRecord> TraceRecordPtr;
class TraceRecord :
/**
* @brief Trace activity of PVRecord.
*
* A record to set the trace value for another record
* It is meant to be used via a channelPutGet request.
* The argument has two fields: recordName and level.
* The result has a field named status.
*/
class epicsShareClass TraceRecord :
public PVRecord
{
public:
POINTER_DEFINITIONS(TraceRecord);
/**
* @brief Factory method to create TraceRecord.
*
* @param recordName The name for the TraceRecord.
* @return A shared pointer to TraceRecord..
*/
static TraceRecordPtr create(
epics::pvData::String const & recordName);
virtual ~TraceRecord();
virtual void destroy();
std::string const & recordName);
/**
* standard init method required by PVRecord
* @return true unless record name already exists.
*/
virtual bool init();
/**
* @brief Set the trace level for record specified by recordName.
*/
virtual void process();
private:
TraceRecord(
epics::pvData::String const & recordName,
std::string const & recordName,
epics::pvData::PVStructurePtr const & pvStructure);
PVDatabasePtr pvDatabase;
epics::pvData::PVStringPtr pvRecordName;
epics::pvData::PVIntPtr pvLevel;
epics::pvData::PVStringPtr pvResult;
bool isDestroyed;
};
}}

11
src/pvAccess/Makefile Normal file
View File

@@ -0,0 +1,11 @@
# This is a Makefile fragment, see ../Makefile
SRC_DIRS += $(PVDATABASE_SRC)/pvAccess
DBD += registerChannelProviderLocal.dbd
LIBSRCS += channelProviderLocal.cpp
LIBSRCS += channelLocal.cpp
LIBSRCS += monitorFactory.cpp
LIBSRCS += registerChannelProviderLocal.cpp

File diff suppressed because it is too large Load Diff

View File

@@ -10,18 +10,25 @@
*/
#include <pv/serverContext.h>
#include <pv/syncChannelFind.h>
#define epicsExportSharedSymbols
#include <pv/channelProviderLocal.h>
#include <pv/traceRecord.h>
namespace epics { namespace pvDatabase {
using namespace epics::pvData;
using namespace epics::pvAccess;
using std::tr1::static_pointer_cast;
using std::tr1::dynamic_pointer_cast;
using std::cout;
using std::endl;
using std::string;
namespace epics { namespace pvDatabase {
static string providerName("local");
static String providerName("local");
class LocalChannelProviderFactory;
typedef std::tr1::shared_ptr<LocalChannelProviderFactory> LocalChannelProviderFactoryPtr;
@@ -31,7 +38,7 @@ class LocalChannelProviderFactory : public ChannelProviderFactory
public:
POINTER_DEFINITIONS(LocalChannelProviderFactory);
virtual String getFactoryName() { return providerName;}
virtual string getFactoryName() { return providerName;}
static LocalChannelProviderFactoryPtr create(
ChannelProviderLocalPtr const &channelProvider)
{
@@ -46,7 +53,7 @@ public:
}
virtual ChannelProvider::shared_pointer newInstance()
{
return channelProvider;
throw std::logic_error("newInstance not Implemented");
}
private:
LocalChannelProviderFactory(
@@ -61,10 +68,15 @@ ChannelProviderLocalPtr getChannelProviderLocal()
static ChannelProviderLocalPtr channelProviderLocal;
static Mutex mutex;
Lock xx(mutex);
if(channelProviderLocal.get()==NULL) {
if(!channelProviderLocal) {
channelProviderLocal = ChannelProviderLocalPtr(
new ChannelProviderLocal());
LocalChannelProviderFactory::create(channelProviderLocal);
ChannelProvider::shared_pointer xxx =
dynamic_pointer_cast<ChannelProvider>(channelProviderLocal);
channelProviderLocal->channelFinder =
SyncChannelFind::shared_pointer(new SyncChannelFind(xxx));
LocalChannelProviderFactoryPtr factory(LocalChannelProviderFactory::create(channelProviderLocal));
}
return channelProviderLocal;
}
@@ -77,62 +89,66 @@ ChannelProviderLocal::ChannelProviderLocal()
ChannelProviderLocal::~ChannelProviderLocal()
{
cout << "~ChannelProviderLocal()" << endl;
destroy();
}
void ChannelProviderLocal::destroy()
{
cout << "ChannelProviderLocal::destroy()" << endl;
Lock xx(mutex);
if(beingDestroyed) return;
beingDestroyed = true;
pvDatabase->destroy();
pvDatabase.reset();
}
String ChannelProviderLocal::getProviderName()
string ChannelProviderLocal::getProviderName()
{
return providerName;
}
ChannelFind::shared_pointer ChannelProviderLocal::channelFind(
String const & channelName,
string const & channelName,
ChannelFindRequester::shared_pointer const &channelFindRequester)
{
Lock xx(mutex);
PVRecordPtr pvRecord = pvDatabase->findRecord(channelName);
if(pvRecord.get()!=NULL) {
if(pvRecord) {
channelFindRequester->channelFindResult(
Status::Ok,
ChannelFind::shared_pointer(),
channelFinder,
true);
} else {
Status notFoundStatus(Status::STATUSTYPE_ERROR,String("pv not found"));
Status notFoundStatus(Status::STATUSTYPE_ERROR,"pv not found");
channelFindRequester->channelFindResult(
notFoundStatus,
ChannelFind::shared_pointer(),
channelFinder,
false);
}
return ChannelFind::shared_pointer();
return channelFinder;
}
ChannelFind::shared_pointer ChannelProviderLocal::channelList(
ChannelListRequester::shared_pointer const & channelListRequester)
{
PVStringArrayPtr records;
{
Lock guard(mutex);
records = pvDatabase->getRecordNames();
}
channelListRequester->channelListResult(Status::Ok, channelFinder, records->view(), false);
return channelFinder;
}
Channel::shared_pointer ChannelProviderLocal::createChannel(
String const & channelName,
string const & channelName,
ChannelRequester::shared_pointer const &channelRequester,
short priority)
{
return createChannel(channelName,channelRequester,priority,"");
}
Channel::shared_pointer ChannelProviderLocal::createChannel(
String const & channelName,
ChannelRequester::shared_pointer const &channelRequester,
short priority,
String const &address)
{
Lock xx(mutex);
PVRecordPtr pvRecord = pvDatabase->findRecord(channelName);
if(pvRecord.get()!=NULL) {
if(pvRecord) {
ChannelLocalPtr channel(new ChannelLocal(
getPtrSelf(),channelRequester,pvRecord));
channelRequester->channelCreated(
@@ -141,11 +157,22 @@ Channel::shared_pointer ChannelProviderLocal::createChannel(
pvRecord->addPVRecordClient(channel);
return channel;
}
Status notFoundStatus(Status::STATUSTYPE_ERROR,String("pv not found"));
Status notFoundStatus(Status::STATUSTYPE_ERROR,"pv not found");
channelRequester->channelCreated(
notFoundStatus,
Channel::shared_pointer());
return Channel::shared_pointer();
}
Channel::shared_pointer ChannelProviderLocal::createChannel(
string const & channelName,
ChannelRequester::shared_pointer const &channelRequester,
short priority,
string const &address)
{
if(!address.empty()) throw std::invalid_argument("address not allowed for local implementation");
return createChannel(channelName, channelRequester, priority);
}
}}

View File

@@ -1,177 +0,0 @@
/* channelProviderLocal.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/**
* @author Marty Kraimer
* @date 2013.04
*/
#ifndef CHANNELPROVIDERLOCAL_H
#define CHANNELPROVIDERLOCAL_H
#include <string>
#include <cstring>
#include <stdexcept>
#include <memory>
#include <set>
#include <pv/lock.h>
#include <pv/pvType.h>
#include <pv/pvData.h>
#include <pv/pvAccess.h>
#include <pv/pvDatabase.h>
#include <pv/status.h>
#include <pv/monitorAlgorithm.h>
namespace epics { namespace pvDatabase {
class MonitorFactory;
typedef std::tr1::shared_ptr<MonitorFactory> MonitorFactoryPtr;
class MonitorLocal;
typedef std::tr1::shared_ptr<MonitorLocal> MonitorLocalPtr;
class ChannelProviderLocal;
typedef std::tr1::shared_ptr<ChannelProviderLocal> ChannelProviderLocalPtr;
class ChannelLocal;
typedef std::tr1::shared_ptr<ChannelLocal> ChannelLocalPtr;
extern MonitorFactoryPtr getMonitorFactory();
class MonitorFactory
{
public:
POINTER_DEFINITIONS(MonitorFactory);
virtual ~MonitorFactory();
virtual void destroy();
epics::pvData::MonitorPtr createMonitor(
PVRecordPtr const & pvRecord,
epics::pvData::MonitorRequester::shared_pointer const & monitorRequester,
epics::pvData::PVStructurePtr const & pvRequest);
void registerMonitorAlgorithmCreate(
MonitorAlgorithmCreatePtr const &monitorAlgorithmCreate);
MonitorAlgorithmCreatePtr getMonitorAlgorithmCreate(
epics::pvData::String algorithmName);
private:
MonitorFactory();
friend class MonitorLocal;
friend MonitorFactoryPtr getMonitorFactory();
std::multiset<MonitorAlgorithmCreatePtr> monitorAlgorithmCreateList;
bool isDestroyed;
epics::pvData::Mutex mutex;
};
extern ChannelProviderLocalPtr getChannelProviderLocal();
class ChannelProviderLocal :
public epics::pvAccess::ChannelProvider,
public std::tr1::enable_shared_from_this<ChannelProviderLocal>
{
public:
POINTER_DEFINITIONS(ChannelProviderLocal);
virtual ~ChannelProviderLocal();
virtual void destroy();
virtual epics::pvData::String getProviderName();
virtual epics::pvAccess::ChannelFind::shared_pointer channelFind(
epics::pvData::String const &channelName,
epics::pvAccess::ChannelFindRequester::shared_pointer const & channelFindRequester);
virtual epics::pvAccess::Channel::shared_pointer createChannel(
epics::pvData::String const &channelName,
epics::pvAccess::ChannelRequester::shared_pointer const &channelRequester,
short priority);
virtual epics::pvAccess::Channel::shared_pointer createChannel(
epics::pvData::String const &channelName,
epics::pvAccess::ChannelRequester::shared_pointer const &channelRequester,
short priority,
epics::pvData::String const &address);
private:
shared_pointer getPtrSelf()
{
return shared_from_this();
}
ChannelProviderLocal();
friend ChannelProviderLocalPtr getChannelProviderLocal();
PVDatabasePtr pvDatabase;
epics::pvData::Mutex mutex;
bool beingDestroyed;
friend class ChannelProviderLocalRun;
};
class ChannelLocal :
public epics::pvAccess::Channel,
public PVRecordClient,
public std::tr1::enable_shared_from_this<ChannelLocal>
{
public:
POINTER_DEFINITIONS(ChannelLocal);
ChannelLocal(
ChannelProviderLocalPtr const &channelProvider,
epics::pvAccess::ChannelRequester::shared_pointer const & requester,
PVRecordPtr const & pvRecord
);
virtual ~ChannelLocal();
virtual void destroy();
virtual epics::pvData::String getRequesterName();
virtual void message(
epics::pvData::String const & message,
epics::pvData::MessageType messageType);
virtual epics::pvAccess::ChannelProvider::shared_pointer getProvider()
{
return provider;
}
virtual epics::pvData::String getRemoteAddress();
virtual epics::pvAccess::Channel::ConnectionState getConnectionState();
virtual epics::pvData::String getChannelName();
virtual epics::pvAccess::ChannelRequester::shared_pointer getChannelRequester();
virtual bool isConnected();
virtual void getField(
epics::pvAccess::GetFieldRequester::shared_pointer const &requester,
epics::pvData::String const & subField);
virtual epics::pvAccess::AccessRights getAccessRights(
epics::pvData::PVField::shared_pointer const &pvField);
virtual epics::pvAccess::ChannelProcess::shared_pointer createChannelProcess(
epics::pvAccess::ChannelProcessRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvAccess::ChannelGet::shared_pointer createChannelGet(
epics::pvAccess::ChannelGetRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvAccess::ChannelPut::shared_pointer createChannelPut(
epics::pvAccess::ChannelPutRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvAccess::ChannelPutGet::shared_pointer createChannelPutGet(
epics::pvAccess::ChannelPutGetRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvAccess::ChannelRPC::shared_pointer createChannelRPC(
epics::pvAccess::ChannelRPCRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvData::Monitor::shared_pointer createMonitor(
epics::pvData::MonitorRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual epics::pvAccess::ChannelArray::shared_pointer createChannelArray(
epics::pvAccess::ChannelArrayRequester::shared_pointer const &requester,
epics::pvData::PVStructurePtr const &pvRequest);
virtual void printInfo();
virtual void printInfo(epics::pvData::StringBuilder out);
virtual void detach(PVRecordPtr const &pvRecord);
protected:
shared_pointer getPtrSelf()
{
return shared_from_this();
}
private:
ChannelProviderLocalPtr provider;
epics::pvAccess::ChannelRequester::shared_pointer requester;
PVRecordPtr pvRecord;
bool beingDestroyed;
epics::pvData::Mutex mutex;
};
}}
#endif /* CHANNELPROVIDERLOCAL_H */

Some files were not shown because too many files have changed in this diff Show More