<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki-ext.aps.anl.gov/epics/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=KayKasemir</id>
	<title>EPICSWIKI - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki-ext.aps.anl.gov/epics/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=KayKasemir"/>
	<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=Special:Contributions/KayKasemir"/>
	<updated>2026-08-30T18:59:41Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.36.1</generator>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=How_to_Configure_Channel_Access&amp;diff=3719</id>
		<title>How to Configure Channel Access</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=How_to_Configure_Channel_Access&amp;diff=3719"/>
		<updated>2013-10-16T21:52:31Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Initial attempt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= How to Configure Channel Access =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Basic Operation, One IOC on same subnet =&lt;br /&gt;
Assume an IOC has a record &amp;quot;fred&amp;quot;, and  you want to use &amp;quot;caget fred&amp;quot; or a similar CA client to read it.&lt;br /&gt;
&lt;br /&gt;
When starting out with one IOC on the network, things are simple:&lt;br /&gt;
&lt;br /&gt;
CA clients will by default broadcast name search requests to UDP port 5064 on the subnet.&lt;br /&gt;
As long as the IOC is on any computer on the subnet, it should eventually receive those search requests. Server and client then establish a TCP connection, and data is exchanged.&lt;br /&gt;
&lt;br /&gt;
= Multiple IOCs on different computers, but same subnet =&lt;br /&gt;
If running multiple IOCs, each on their own computer, on the same subnet, the basic broadcast name search will still succeed, no change necessary.&lt;br /&gt;
&lt;br /&gt;
= IOCs on different subnets =&lt;br /&gt;
The default broadcast name search is limited to the subnet of the computer running the CA client.&lt;br /&gt;
To reach IOCs on one or more additional subnets, the environment variable EPICS_CA_ADDR_LIST needs to be configured.&lt;br /&gt;
It can list either the specific IP addresses of each IOC, or the broadcast address of their subnet.&lt;br /&gt;
Note, however, that routers will often not forward broadcast requests, which suggests using specific IP addresses.&lt;br /&gt;
&lt;br /&gt;
= Multiple IOCs on the same computer =&lt;br /&gt;
When starting the first IOC on a computer, it will listen to name searches on UDP port  5064.&lt;br /&gt;
When starting a second IOC on the same computer, it will also listen to name searches on UDP port  5064.&lt;br /&gt;
Due to limitations in most network kernels, however, only the IOC started ''last'' will actually receive UDP search requests that are sent to that computer, port 5064. As a workaround, you need to configure the EPICS_CA_ADDR_LIST to use the broadcast address of the respective subnet.&lt;br /&gt;
&lt;br /&gt;
= Multiple IOCs on the same computer but on a different subnet =&lt;br /&gt;
Combining the last two points results in a problem:&lt;br /&gt;
To reach multiple IOCs on the same computer, EPICS_CA_ADDR_LIST must be set to the broadcast address of that computer's subnet.&lt;br /&gt;
If that subnet, however, is different from the CA client's subnet, routers tend to not allow such broadcast traffic.&lt;br /&gt;
&lt;br /&gt;
There are several options to solve this:&lt;br /&gt;
&lt;br /&gt;
== Channel Access Gateway ==&lt;br /&gt;
The gateway, running on the subnet that has the desired IOCs, will use the broadcast address of that subnet in its EPICS_CA_ADDR_LIST, so it can reach all IOCs, including multiple IOCs running on the same computer, throughout that subnet.&lt;br /&gt;
The CA client on a different subnet uses only  EPICS_CA_ADDR_LIST=ip-of-the-gateway to directly reach the gateway, which is possible via routers.&lt;br /&gt;
&lt;br /&gt;
In addition to establishing the basic connectivity, the gateway also offers IOC load reduction and it can add access security, for example limit write access.&lt;br /&gt;
&lt;br /&gt;
== CA Nameserver ==&lt;br /&gt;
You can run a CA Name Server in the GUI subnet which knows about the IOCs and responds to search requests; in this case you would *not* set the EPICS_CA_ADDR_LIST variables. This is almost equivalent to running a CA Gateway, but is slightly more robust in that if the Nameserver process dies it wouldn't kill any existing connections.&lt;br /&gt;
&lt;br /&gt;
== UDP Broadcast Packet Relay ==&lt;br /&gt;
If you have access to a machine with a network interface on both subnets you could also try running a program on it called UDP Broadcast Packet Relay https://www.joachim-breitner.de/udp-broadcast-relay/ which forwards broadcast packets between subnets. You would probably have to run it twice, once on port 5064 and once on 5065; the first one will forward CA search requests between the subnets, and the second sends the CA beacons. If you do have success with this please let us know, we use it here at the APS for forwarding timing UDP packets but not for CA.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Firewalls =&lt;br /&gt;
Firewalls may need to be configured to pass the UDP and TCP 5064 traffic.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The channel access reference manual, &lt;br /&gt;
http://www.aps.anl.gov/epics/base/R3-14/8-docs/CAref.html&lt;br /&gt;
provides a lot more detail.&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=HowTo_Documents&amp;diff=3718</id>
		<title>HowTo Documents</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=HowTo_Documents&amp;diff=3718"/>
		<updated>2013-10-16T21:29:17Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: How to Configure Channel Access...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is a directory of various How-To documents written by members of the EPICS collaboration.  Contributions to this page are most welcome!&lt;br /&gt;
&lt;br /&gt;
=== EPICS Base on Different Architectures and Operating Systems ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.aps.anl.gov/epics/base/RTEMS/tutorial/ Getting Started with EPICS on RTEMS]&lt;br /&gt;
* [[HowToPC104|Getting Started with R3.14.7 on a PC104 running Linux]]&lt;br /&gt;
* [[How To Port EPICS to a new OS/Architecture]]&lt;br /&gt;
* [[How To Use Posix Thread Priority Scheduling under Linux]]&lt;br /&gt;
* [[How To cross compile EPICS and a IOC to an old x86 Linux system]]&lt;br /&gt;
&lt;br /&gt;
=== Drivers and Device Support ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.aps.anl.gov/epics/modules/soft/asyn/HowToDoSerial_StreamDevice.html How To Do Serial (using Asyn Driver and StreamDevice)] (also covers VXI-11)&lt;br /&gt;
* [http://www.aps.anl.gov/epics/modules/soft/asyn/R4-15/HowToDoSerial/tutorial.pdf How To Do Serial (using Asyn Driver and devGPIB)]&lt;br /&gt;
* [[How to make your EPICS driver operating system independent]]&lt;br /&gt;
* [[How To Write Device Support that uses Asyn Driver]] ''(Incomplete!)''&lt;br /&gt;
* [[How to use GPIB ports with linux-gpib and StreamDevice]]&lt;br /&gt;
* [[How to avoid copying arrays with waveformRecord]]&lt;br /&gt;
&lt;br /&gt;
=== Applications ===&lt;br /&gt;
&lt;br /&gt;
* [[Common Database patterns]]&lt;br /&gt;
* [[How To Install Channel Archiver On Scientific Linux]]&lt;br /&gt;
* [[What PV Save and Restore Tools are available]]&lt;br /&gt;
* [[How to Add a New Breakpoint Table]]&lt;br /&gt;
* [[&amp;quot;Best Practice&amp;quot; Guidelines]]&lt;br /&gt;
* [[How do I get EPICS applications to work with a Mac OS X firewall?]]&lt;br /&gt;
&lt;br /&gt;
=== Infrastructure and Other Stuff ===&lt;br /&gt;
&lt;br /&gt;
* [[How To Set Up a Linux Box as an IOC Boot Server]]&lt;br /&gt;
* [[How To Set Up a Mirror of the EPICS Web Site]]&lt;br /&gt;
* [[How to Set Up a Soft IOC Framework on Linux]]&lt;br /&gt;
* [[How to Configure Channel Access]]&lt;br /&gt;
* [[How to Set Up Console Access and Logging for VME and Soft IOCs]]&lt;br /&gt;
* [[How to Set Up NAL (Nagios Alarm Handler) to monitor an EPICS network]]&lt;br /&gt;
&lt;br /&gt;
=== Collaboration Stuff ===&lt;br /&gt;
&lt;br /&gt;
* [[How to run an EPICS Collaboration Meeting]]&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=What_PV_Save_and_Restore_Tools_are_available&amp;diff=1416</id>
		<title>What PV Save and Restore Tools are available</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=What_PV_Save_and_Restore_Tools_are_available&amp;diff=1416"/>
		<updated>2007-06-28T15:36:40Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;There are a number of different tools available within the EPICS community for saving and restoring values of PVs.  This page gives somewhere for them to be briefly described, since they all have somewhat different characteristics.  This page may be incomplete; if you know of a published tool that is not described here, please consider adding it, or email &amp;lt;volunteer anyone?&amp;gt; about it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== IOC-based Tools ==&lt;br /&gt;
&lt;br /&gt;
=== [http://www.aps.anl.gov/aod/bcda/synApps/autosave/autosave.html SynApps Autosave] ===&lt;br /&gt;
&lt;br /&gt;
Autosave automatically saves the values of EPICS process variables (PVs) to files on a server, and restores those values when the IOC is rebooted.&lt;br /&gt;
&lt;br /&gt;
=== ... ===&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
== Host-based Tools ==&lt;br /&gt;
&lt;br /&gt;
=== XAL 'score' ===&lt;br /&gt;
The XAL collection of high-level applications includes 'score':&lt;br /&gt;
A Table of PV names, each with a current value and a saved value.&lt;br /&gt;
One can see which PVs differ from their saved state,&lt;br /&gt;
restore them etc.&lt;br /&gt;
&lt;br /&gt;
Don't know a good web page, but try&lt;br /&gt;
[http://neutrons.ornl.gov/APGroup/appProg/xal/xal.htm this one ].&lt;br /&gt;
&lt;br /&gt;
=== CSS 'PV Table' ===&lt;br /&gt;
Similar to 'score', but simpler and still unter development.&lt;br /&gt;
Saves values to XML files, no connection to RDB.&lt;br /&gt;
&lt;br /&gt;
For more on  CSS (Control System Studio),&lt;br /&gt;
see the [http://css.desy.de main page at DESY].&lt;br /&gt;
&lt;br /&gt;
=== ... ===&lt;br /&gt;
&lt;br /&gt;
...&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=What_PV_Save_and_Restore_Tools_are_available&amp;diff=1415</id>
		<title>What PV Save and Restore Tools are available</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=What_PV_Save_and_Restore_Tools_are_available&amp;diff=1415"/>
		<updated>2007-06-28T15:33:56Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;There are a number of different tools available within the EPICS community for saving and restoring values of PVs.  This page gives somewhere for them to be briefly described, since they all have somewhat different characteristics.  This page may be incomplete; if you know of a published tool that is not described here, please consider adding it, or email &amp;lt;volunteer anyone?&amp;gt; about it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== IOC-based Tools ==&lt;br /&gt;
&lt;br /&gt;
=== [http://www.aps.anl.gov/aod/bcda/synApps/autosave/autosave.html SynApps Autosave] ===&lt;br /&gt;
&lt;br /&gt;
Autosave automatically saves the values of EPICS process variables (PVs) to files on a server, and restores those values when the IOC is rebooted.&lt;br /&gt;
&lt;br /&gt;
=== ... ===&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
== Host-based Tools ==&lt;br /&gt;
&lt;br /&gt;
=== XAL 'score' ===&lt;br /&gt;
The XAL collection of high-level applications includes 'score':&lt;br /&gt;
A Table of PV names, each with a current value and a saved value.&lt;br /&gt;
One can see which PVs differ from their saved state,&lt;br /&gt;
restore them etc.&lt;br /&gt;
&lt;br /&gt;
=== CSS 'PV Table' ===&lt;br /&gt;
Similar to 'score', but simpler and still unter development.&lt;br /&gt;
Saves values to XML files, no connection to RDB.&lt;br /&gt;
&lt;br /&gt;
=== ... ===&lt;br /&gt;
&lt;br /&gt;
...&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Use_Cases&amp;diff=2757</id>
		<title>V4 Use Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Use_Cases&amp;diff=2757"/>
		<updated>2005-10-11T15:05:51Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Added thoughts from EPICS meeting&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
These are example use cases where we need to assert that each layer of EPICS V4 handles them:&lt;br /&gt;
&lt;br /&gt;
1. Can the CA client user API articulate the request?&lt;br /&gt;
&lt;br /&gt;
2. Can the network protocol transfer the request?&lt;br /&gt;
&lt;br /&gt;
3. Do the database &amp;amp; drivers understand the request and provide the necessary data?&lt;br /&gt;
&lt;br /&gt;
4. Does the data get back up to the client in the appropriate form and at the requested time?&lt;br /&gt;
&lt;br /&gt;
== Use Cases ==&lt;br /&gt;
&lt;br /&gt;
=== Command-line tools for debugging and scripting ===&lt;br /&gt;
1. List all channels that match a given name pattern.&lt;br /&gt;
&lt;br /&gt;
2. Dump the current &amp;quot;value&amp;quot; property of the channel &amp;quot;fred&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
3. Dump all properties, their type, and current value for a given channel.&lt;br /&gt;
&lt;br /&gt;
4. Dump the timestamp, value, units, status, severity of &amp;quot;fred&amp;quot; once a second.&lt;br /&gt;
&lt;br /&gt;
5. ... whenever any of the timestamp, ... severity change.&lt;br /&gt;
&lt;br /&gt;
6. List all events that a PV supports: On change, 1Hz, RF Pulse, Beam Pulse, ...&lt;br /&gt;
&lt;br /&gt;
7. Dump the value of the cavity waveform for every beam pulse.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Display Manager ===&lt;br /&gt;
1. Display an analog input value on a bar:&amp;lt;br&amp;gt;&lt;br /&gt;
Get limits, subscribe to values, receive value &amp;amp; display it, receive change-of-display-range notification,...&lt;br /&gt;
&lt;br /&gt;
2. Display a menu output for a 2 state valve on a text update:&amp;lt;br&amp;gt;&lt;br /&gt;
Get state strings, subscribe to (numeric?) value, display incoming data, receive state string updates, ...&lt;br /&gt;
&lt;br /&gt;
3. Give a controller for a 2 state valve controller on a radio button.&amp;lt;br&amp;gt;Handle same as 2?&lt;br /&gt;
&lt;br /&gt;
4. Display a waveform on an xy plot.&amp;lt;br&amp;gt;&lt;br /&gt;
Similar to 1, but array data?&lt;br /&gt;
&lt;br /&gt;
5. Attempt to display a string on a bar&lt;br /&gt;
&lt;br /&gt;
6. Universal PV Inspector:&amp;lt;br&amp;gt;&lt;br /&gt;
Get properties, allow user to select properties.&amp;lt;br&amp;gt;&lt;br /&gt;
If writeable string -&amp;gt; display text entry field.&amp;lt;br&amp;gt;&lt;br /&gt;
If read-only string -&amp;gt; display text update field.&amp;lt;br&amp;gt;&lt;br /&gt;
If writeable numeric with limits -&amp;gt; display slider with range of number.&amp;lt;br&amp;gt;&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
7. Display as bars, a list of BPMs where I can set the offset in time to fetch (assuming that these BPMs are read as arrays that are all triggered at one time – but I want to see them at different points in time as it goes down the beamline).&lt;br /&gt;
&lt;br /&gt;
=== Physics Application ===&lt;br /&gt;
&lt;br /&gt;
BPM-related ideas&lt;br /&gt;
&lt;br /&gt;
1. List all channels available that are BPMs.&lt;br /&gt;
&lt;br /&gt;
2.  Select all of them in sector 6&lt;br /&gt;
&lt;br /&gt;
3. Get the values of all BPMs in a sector, once, as a snapshot. This means all the BPMs must be read at the same time, for the same beam pulse, or whatever a specific site considers &amp;quot;at once&amp;quot;.&lt;br /&gt;
A variant would be: Display them when the beam is going to the end station A (one of 8 destinations possible)&lt;br /&gt;
&lt;br /&gt;
4. Same, but display them periodically, for example on the .1 Hz event&lt;br /&gt;
&lt;br /&gt;
5. Find the corrector magnet PVs that belong to a list of BPMs. Read the BPMs and current maget settings &amp;quot;at once&amp;quot;, compute new settings, write the new settings &amp;quot;at once&amp;quot;.&lt;br /&gt;
&amp;lt;p&amp;gt; &lt;br /&gt;
Scan a probe in the beam line&lt;br /&gt;
&lt;br /&gt;
1. Find PVs for some beam line device:&amp;lt;br&amp;gt;&lt;br /&gt;
Stepper motors for probe's X &amp;amp; Y position, intensity of beam transmitted by probe,&lt;br /&gt;
beam shutter open/close, beam available&lt;br /&gt;
&lt;br /&gt;
2. Scan sequence:&amp;lt;br&amp;gt;&lt;br /&gt;
 Assert shutter is closed;&lt;br /&gt;
 move X/Y to 0/0, wait for completion&lt;br /&gt;
 until done:&lt;br /&gt;
    open shutter, wait until open&lt;br /&gt;
    measure intensity for 1 second while beam_available&lt;br /&gt;
    close shutter&lt;br /&gt;
    move to next position, wait until you get there&lt;br /&gt;
&lt;br /&gt;
=== Archiver ===&lt;br /&gt;
1. Get value, timestamp, status/severity of a channel once per second&lt;br /&gt;
&lt;br /&gt;
2. Archive default properties of a channel using the default deadband.&amp;lt;br&amp;gt;&lt;br /&gt;
How are they defined? How does the archiver learn about them?&lt;br /&gt;
&lt;br /&gt;
3. Given that there is a new view defined in the stepper motor that includes all motor readback data:Subscribe to counterClockWiseLimit, clockWiseLimit, moving, doneRetries, homeLimit at 10 Hz.&lt;br /&gt;
&lt;br /&gt;
=== Infrastructure for various applications ===&lt;br /&gt;
1. Serialize and de-serialize the data type description in human-readable form.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1.1 Used to get common syntax for user-input to command line tools or for the archiver config file,  where users need to describe what to get/put/archive and how:&amp;lt;br&amp;gt;&lt;br /&gt;
 'value' as double, 'units' as string, ...&lt;br /&gt;
&lt;br /&gt;
1.2 Used to get common output from tools that 'dump' the data type for info or debug purposes.&lt;br /&gt;
&lt;br /&gt;
2. Serialize and de-... in rather compact form&lt;br /&gt;
&lt;br /&gt;
2.1 Used by data storage tools to store the type information in the header of data blocks.&lt;br /&gt;
&lt;br /&gt;
2.2 Maybe also used by the network layer to initially xfer the data types.&lt;br /&gt;
&lt;br /&gt;
Could those formats be the same, i.e. a short yet human readable format?&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;{d:value,s:units,{d:low,d:high}limits}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
could describe this structure:&lt;br /&gt;
   {&lt;br /&gt;
     double value;&lt;br /&gt;
     string units;&lt;br /&gt;
     struct&lt;br /&gt;
     {&lt;br /&gt;
       double low;&lt;br /&gt;
       double high;&lt;br /&gt;
     }      limits;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
3. Need to provide standard serialization of the data type description _and_ the data? Or should the developers of the network layer and archiver each invent their own?&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Data_Interface&amp;diff=2759</id>
		<title>V4 Data Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Data_Interface&amp;diff=2759"/>
		<updated>2005-09-02T20:21:08Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: DA code example&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Issue ==&lt;br /&gt;
I think the following data interface is easier to understand&lt;br /&gt;
than the data access approach.&lt;br /&gt;
&lt;br /&gt;
Am I wrong, because Data Access is really much easier?&lt;br /&gt;
&lt;br /&gt;
Am I right, but the approach shown here will never work?&lt;br /&gt;
&lt;br /&gt;
Would it work, but performance would be unacceptable?&lt;br /&gt;
&lt;br /&gt;
[[Image:V4DataInter.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Data Interface Classes ===&lt;br /&gt;
 &lt;br /&gt;
 // Description of basic data character&lt;br /&gt;
 enum EpicsDataCharacter&lt;br /&gt;
 {&lt;br /&gt;
     Octet,     // Raw bits,known to a server/client pair, but no one else&lt;br /&gt;
     Bool,&lt;br /&gt;
     Integer,   // Discrete, available with 16, 32, 64 bits&lt;br /&gt;
     Real,      // Floating-point, available with 32 and 64 bits&lt;br /&gt;
     String,    // UTF-8&lt;br /&gt;
     Enum,      // &amp;gt;=0 Integer with state names&lt;br /&gt;
     Structure,&lt;br /&gt;
     TimeStamp  // For efficiency?&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // Full description of the data&lt;br /&gt;
 // This could be a pure interface,&lt;br /&gt;
 // while Ben suggests to use a struct/class&lt;br /&gt;
 // so that the user can easier get it as a whole.&lt;br /&gt;
 // Ben also suggests that getNumDims throws an&lt;br /&gt;
 // exception for scalars.&lt;br /&gt;
 // I think a scalar has dimension 1, size 1,&lt;br /&gt;
 // no exception.&lt;br /&gt;
 class EpicsDataDescriptor&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual EpicsDataCharacter getType() = 0;&lt;br /&gt;
     virtual int  getNumDims() = 0;      // Scalar, vector: 1, matrix: &amp;gt;1&lt;br /&gt;
     virtual int  getSize(int dim) = 0;  // Scalar: 1&lt;br /&gt;
     virtual int  getBitsize() = 0;      // bits of integer or real. 0 for rest?&lt;br /&gt;
     virtual bool isSigned() = 0;        // Integer might be unsigned?&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // When the exact size-locked types are needed,&lt;br /&gt;
 // use these integer types from stdint.h:&lt;br /&gt;
 //     int8_t, int16_t, int32_t, int64_t&lt;br /&gt;
 // and the following:&lt;br /&gt;
 typedef bool          bool_t;&lt;br /&gt;
 typedef float         real32_t;&lt;br /&gt;
 typedef double        real64_t;&lt;br /&gt;
&lt;br /&gt;
 // Read-Access to the data.&lt;br /&gt;
 // This one tries to allow as many conversions as possible.&lt;br /&gt;
 // Given for example an underlying Real, 64bit, scalar,&lt;br /&gt;
 // one can ask for a real64 but also int, string, ...&lt;br /&gt;
 // You can also ask for the 5th element of a double array&lt;br /&gt;
 // or try to access the structure detail,&lt;br /&gt;
 // but then you have to expect a failed response.&lt;br /&gt;
 // So a simple client uses&lt;br /&gt;
 //    if (! getDouble()) { .. error .. }&lt;br /&gt;
 // and a more sopisticated one uses&lt;br /&gt;
 // getType() == Real or Integer  --&amp;gt; getDouble()&lt;br /&gt;
 //&lt;br /&gt;
 // The alternative would be individual reader interfaces&lt;br /&gt;
 // for numerics, strings, structures, numeric arrays, string arrays, ...&lt;br /&gt;
 // or even separate ones for int8, int16, ...&lt;br /&gt;
 // Marty's &amp;quot;network accessable data&amp;quot; email to core talk did the latter.&lt;br /&gt;
 // JCA also currently goes that way:&lt;br /&gt;
 // getDBR() == DOUBLE ?   --&amp;gt;   ( (DBR_Double)getValue() ).getDoubleValue()&lt;br /&gt;
 // getDBR() == INT    ?   --&amp;gt;   ( (DBR_Int)getValue() ).getIntValue()&lt;br /&gt;
 class EpicsDataReader : public EpicsDataDescriptor&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     // Get data as type XX as best as possible.&lt;br /&gt;
     // Example: getDouble returns true for&lt;br /&gt;
     //          Bool (d=0.0, 1.0), Integer, Real, Enum.&lt;br /&gt;
     virtual bool getReal64(real64_t &amp;amp;r) = 0;&lt;br /&gt;
     virtual bool getDouble(double &amp;amp;d) = 0;&lt;br /&gt;
     virtual bool getInt(int &amp;amp;d) = 0;&lt;br /&gt;
     // String&lt;br /&gt;
     virtual bool getString(char *buf, int buf_len) = 0;&lt;br /&gt;
     virtual int  getStringLength() = 0;&lt;br /&gt;
     virtual bool getCstring(const char *&amp;amp;c_ptr) = 0;&lt;br /&gt;
     /// many more, at least one for each supported type.&lt;br /&gt;
&lt;br /&gt;
     // A structure is accessed via a (sub-)catalog.&lt;br /&gt;
     // See below for PropertyCatalog.&lt;br /&gt;
     virtual class EpicsPropertyCatalog *getStructure() = 0;&lt;br /&gt;
     &lt;br /&gt;
     // Get array element. getDouble(x) == getArrayDouble(0, x)&lt;br /&gt;
     virtual bool getArrayDouble(int element, double &amp;amp;d) = 0;&lt;br /&gt;
     virtual bool getMatrixDouble(int dim, int element, double &amp;amp;d) = 0;&lt;br /&gt;
     virtual class EpicsPropertyCatalog *getArrayStructure(int element) = 0;&lt;br /&gt;
     /// For arrays, it could include per-element access&lt;br /&gt;
     /// as well as accessors that copy sub-arrays into client buffers.&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // &amp;quot;Property&amp;quot; has a name and data reader interface&lt;br /&gt;
 class EpicsProperty : public EpicsDataReader&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual const char *getName() = 0;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // A catalog of properties&lt;br /&gt;
 class EpicsPropertyCatalog&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual int            getNumProperties() = 0;&lt;br /&gt;
     virtual EpicsProperty *findProperty(const char *name) = 0;&lt;br /&gt;
     virtual EpicsProperty *getProperty(int index) = 0;&lt;br /&gt;
     /// Could also support name &amp;lt;-&amp;gt; ID conversions and&lt;br /&gt;
     /// access via numeric ID.&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
=== C++ Implementation ===&lt;br /&gt;
 // On the first read, skip to the 'Usage Example' below.&lt;br /&gt;
 // This code isn't the greatest, but simply here to get&lt;br /&gt;
 // example code that actually compiles &amp;amp; runs.&lt;br /&gt;
 #include &amp;lt;string&amp;gt;&lt;br /&gt;
 using std::string;&lt;br /&gt;
 #include &amp;lt;string.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Base class for the following EpicsProperty implementations.&lt;br /&gt;
 // Provides default handlers for everything.&lt;br /&gt;
 class EpicsPropertyBase : public EpicsProperty&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsPropertyBase(const char *name)&lt;br /&gt;
     {   this-&amp;gt;name = name; }&lt;br /&gt;
&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     // defaults for scalar&lt;br /&gt;
     int getNumDims()&lt;br /&gt;
     {   return 1; }&lt;br /&gt;
     int getSize(int dim)&lt;br /&gt;
     {   return dim==0 ? 1 : 0; }&lt;br /&gt;
     int getBitsize()&lt;br /&gt;
     {   return 0; }&lt;br /&gt;
     bool isSigned()&lt;br /&gt;
     {   return true; }&lt;br /&gt;
&lt;br /&gt;
     // DataReader&lt;br /&gt;
     bool getReal64(real64_t &amp;amp;r)&lt;br /&gt;
     {   return getDouble(r); }&lt;br /&gt;
     bool getDouble(double &amp;amp;d)&lt;br /&gt;
     {&lt;br /&gt;
         int i;&lt;br /&gt;
         if (!getInt(i))&lt;br /&gt;
             return false;&lt;br /&gt;
         d = i;&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     bool getInt(int &amp;amp;i)&lt;br /&gt;
     {&lt;br /&gt;
         double d;&lt;br /&gt;
         if (!getDouble(d))&lt;br /&gt;
             return false;&lt;br /&gt;
         i = (int)d;&lt;br /&gt;
         return true;&lt;br /&gt;
     }    &lt;br /&gt;
     bool getString(char *buf, int buf_len)&lt;br /&gt;
     {&lt;br /&gt;
         const char *ptr;&lt;br /&gt;
         if (!getCstring(ptr))&lt;br /&gt;
             return false;&lt;br /&gt;
         strncpy(buf, ptr, buf_len-1);&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     int  getStringLength()&lt;br /&gt;
     {   return 0; }&lt;br /&gt;
     bool getCstring(const char *&amp;amp;c_ptr)&lt;br /&gt;
     {   return false; }&lt;br /&gt;
     EpicsPropertyCatalog *getStructure()&lt;br /&gt;
     {   return 0; }&lt;br /&gt;
     bool getArrayDouble(int element, double &amp;amp;d)&lt;br /&gt;
     {   return (element == 0) ? getDouble(d) : false; }&lt;br /&gt;
     bool getMatrixDouble(int dim, int element, double &amp;amp;d)&lt;br /&gt;
     {   return (dim == 0) ? getArrayDouble(element, d) : false; }&lt;br /&gt;
     EpicsPropertyCatalog *getArrayStructure(int element)&lt;br /&gt;
     {   return element==0 ? getStructure() : 0; }&lt;br /&gt;
&lt;br /&gt;
     // EpicsProperty&lt;br /&gt;
     const char *getName()&lt;br /&gt;
     {   return name.c_str(); }&lt;br /&gt;
 private:&lt;br /&gt;
        string name;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // EpicsProperty for 'double'. Actual data is outside,&lt;br /&gt;
 // the EpicsDoubleProperty keeps a pointer to it.&lt;br /&gt;
 class EpicsDoubleProperty : public EpicsPropertyBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsDoubleProperty(const char *name, const double *p)&lt;br /&gt;
             : EpicsPropertyBase(name)&lt;br /&gt;
     {   this-&amp;gt;p = p; }&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     EpicsDataCharacter getType()&lt;br /&gt;
     {   return Real; }&lt;br /&gt;
     int getBitsize()&lt;br /&gt;
     {   return sizeof(double)*8; }&lt;br /&gt;
     // EpicsDataReader&lt;br /&gt;
     bool getDouble(double &amp;amp;d)&lt;br /&gt;
     {   d = *p;&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     const double *p;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 // Handler for string. Data is not owned by this class.&lt;br /&gt;
 class EpicsStringProperty : public EpicsPropertyBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsStringProperty(const char *name, const string *p)&lt;br /&gt;
             : EpicsPropertyBase(name)&lt;br /&gt;
     {   this-&amp;gt;p = p; }&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     EpicsDataCharacter getType()&lt;br /&gt;
     {   return String; }&lt;br /&gt;
     // EpicsDataReader&lt;br /&gt;
     int  getStringLength()&lt;br /&gt;
     {   return p-&amp;gt;length(); }&lt;br /&gt;
     virtual bool getCstring(const char *&amp;amp;c_ptr)&lt;br /&gt;
     {&lt;br /&gt;
         c_ptr = p-&amp;gt;c_str();&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     const string *p;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 class EpicsPropertyCatalogBase : public EpicsPropertyCatalog&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsPropertyCatalogBase(int num_properties, EpicsProperty **properties)&lt;br /&gt;
             : num_properties(num_properties), properties(properties)&lt;br /&gt;
     {}&lt;br /&gt;
     // EpicsPropertyCatalog&lt;br /&gt;
     int getNumProperties()&lt;br /&gt;
     {   return num_properties; }&lt;br /&gt;
     EpicsProperty *findProperty(const char *name)&lt;br /&gt;
     {&lt;br /&gt;
         int i;&lt;br /&gt;
         for (i=0; i&amp;lt;num_properties; ++i)&lt;br /&gt;
             if (!strcmp(properties[i]-&amp;gt;getName(), name))&lt;br /&gt;
                 return properties[i];&lt;br /&gt;
         return 0;&lt;br /&gt;
     }&lt;br /&gt;
     EpicsProperty *getProperty(int index)&lt;br /&gt;
     {&lt;br /&gt;
         return (index &amp;gt;=0  &amp;amp;&amp;amp;  index &amp;lt; num_properties) ? properties[index] : 0;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     int            num_properties;&lt;br /&gt;
     EpicsProperty **properties;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 // Handler for struct. Keeps pointers to EpicsProperty&lt;br /&gt;
 // interfaces for the struct's fields.&lt;br /&gt;
 class EpicsStructureProperty : public EpicsPropertyBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsStructureProperty(const char *name, int num_properties, EpicsProperty **properties)&lt;br /&gt;
             : EpicsPropertyBase(name), pc(num_properties, properties)&lt;br /&gt;
     {}&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     EpicsDataCharacter getType()&lt;br /&gt;
     {   return Structure; }&lt;br /&gt;
     // EpicsDataReader&lt;br /&gt;
     EpicsPropertyCatalog *getStructure()&lt;br /&gt;
     {   return &amp;amp;pc; }&lt;br /&gt;
 private:&lt;br /&gt;
     EpicsPropertyCatalogBase pc;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
=== Example for data known at compile-time ===&lt;br /&gt;
 // This is the code that provides a property catalog.&lt;br /&gt;
 // Again skip on the first read.&lt;br /&gt;
 //&lt;br /&gt;
 // All of this could be generated from DBD information.&lt;br /&gt;
 class TestRecord : public EpicsPropertyCatalogBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     TestRecord();&lt;br /&gt;
     // Unsure if database code with pointer to the record&lt;br /&gt;
     // should still directly access the data,&lt;br /&gt;
     // or if the data should be protected/private.&lt;br /&gt;
     double value;&lt;br /&gt;
     string units;&lt;br /&gt;
     struct&lt;br /&gt;
     {&lt;br /&gt;
         double x, y;&lt;br /&gt;
     }   point;&lt;br /&gt;
  private:&lt;br /&gt;
     EpicsProperty *properties[3];&lt;br /&gt;
     EpicsProperty *point_properties[2];&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 TestRecord::TestRecord() &lt;br /&gt;
         : EpicsPropertyCatalogBase(3, properties)&lt;br /&gt;
 {&lt;br /&gt;
     point_properties[0] = new EpicsDoubleProperty(&amp;quot;x&amp;quot;, &amp;amp;point.x);&lt;br /&gt;
     point_properties[1] = new EpicsDoubleProperty(&amp;quot;y&amp;quot;, &amp;amp;point.y);&lt;br /&gt;
     properties[0] = new EpicsDoubleProperty(&amp;quot;value&amp;quot;, &amp;amp;value);&lt;br /&gt;
     properties[1] = new EpicsStringProperty(&amp;quot;units&amp;quot;, &amp;amp;units);&lt;br /&gt;
     properties[2] = new EpicsStructureProperty(&amp;quot;point&amp;quot;, 2, point_properties);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Example Generic 'Dumper' ===&lt;br /&gt;
&lt;br /&gt;
 class EpicsPropertyDumper&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     static const char *type2string(EpicsDataCharacter c)&lt;br /&gt;
     {&lt;br /&gt;
         const char *strings[] =&lt;br /&gt;
         {&lt;br /&gt;
             &amp;quot;Octet&amp;quot;,&lt;br /&gt;
             &amp;quot;Bool&amp;quot;,&lt;br /&gt;
             &amp;quot;Integer&amp;quot;,&lt;br /&gt;
             &amp;quot;Real&amp;quot;,&lt;br /&gt;
             &amp;quot;String&amp;quot;,&lt;br /&gt;
             &amp;quot;Enum&amp;quot;,&lt;br /&gt;
             &amp;quot;Structure&amp;quot;,&lt;br /&gt;
             &amp;quot;TimeStamp&amp;quot;&lt;br /&gt;
         };&lt;br /&gt;
         &lt;br /&gt;
         if (c &amp;lt; 0  ||  c &amp;gt;= sizeof(strings)/sizeof(const char *))&lt;br /&gt;
             return &amp;quot;&amp;lt;unknown&amp;gt;&amp;quot;;&lt;br /&gt;
         return strings[c];&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     static void dump(EpicsPropertyCatalog *pc)&lt;br /&gt;
     {   dump(0, pc); }&lt;br /&gt;
 private:&lt;br /&gt;
     static void indent(int level)&lt;br /&gt;
     {   for (int i=0; i&amp;lt;level; ++i)  printf(&amp;quot;    &amp;quot;); }&lt;br /&gt;
     static void dump(int level, EpicsPropertyCatalog *pc)&lt;br /&gt;
     {&lt;br /&gt;
         double d;&lt;br /&gt;
         int i;&lt;br /&gt;
         for (i=0; i&amp;lt;pc-&amp;gt;getNumProperties(); ++i)&lt;br /&gt;
         { &lt;br /&gt;
            indent(level);&lt;br /&gt;
            EpicsProperty *p = pc-&amp;gt;getProperty(i);&lt;br /&gt;
            EpicsDataCharacter type = p-&amp;gt;getType();&lt;br /&gt;
            printf(&amp;quot;%s (%s, %d %s, %d bits) = &amp;quot;,&lt;br /&gt;
                   p-&amp;gt;getName(), type2string(type),&lt;br /&gt;
                   p-&amp;gt;getSize(0),&lt;br /&gt;
                   (p-&amp;gt;getSize(0) == 1 ? &amp;quot;element&amp;quot; : &amp;quot;elements&amp;quot;),&lt;br /&gt;
                   p-&amp;gt;getBitsize());&lt;br /&gt;
            switch (type)&lt;br /&gt;
            {&lt;br /&gt;
            case String:&lt;br /&gt;
                const char *s;&lt;br /&gt;
                if (p-&amp;gt;getCstring(s))&lt;br /&gt;
                    printf(&amp;quot;'%s' (%d chars)\n&amp;quot;, s, p-&amp;gt;getStringLength());&lt;br /&gt;
                else&lt;br /&gt;
                    printf(&amp;quot;&amp;lt;cannot get string&amp;gt;\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            case Bool:&lt;br /&gt;
            case Integer:&lt;br /&gt;
            case Real:&lt;br /&gt;
                double d;&lt;br /&gt;
                if (p-&amp;gt;getDouble(d))&lt;br /&gt;
                    printf(&amp;quot;%.2f\n&amp;quot;, d);&lt;br /&gt;
                else&lt;br /&gt;
                    printf(&amp;quot;&amp;lt;cannot get double&amp;gt;\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            case Structure:&lt;br /&gt;
                indent(level);&lt;br /&gt;
                printf(&amp;quot;\n{\n&amp;quot;);&lt;br /&gt;
                dump(level+1, p-&amp;gt;getStructure());&lt;br /&gt;
                indent(level);&lt;br /&gt;
                printf(&amp;quot;}\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            default:&lt;br /&gt;
                printf(&amp;quot;&amp;lt;unhandled type %d&amp;gt;\n&amp;quot;, p-&amp;gt;getType());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 }; &lt;br /&gt;
 &lt;br /&gt;
=== Usage Example ===&lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 int main()&lt;br /&gt;
 {&lt;br /&gt;
     TestRecord fred;&lt;br /&gt;
     // Record/device support might have direct access to the fields:&lt;br /&gt;
     fred.value = 42.3;&lt;br /&gt;
     fred.units = &amp;quot;a.u.&amp;quot;;&lt;br /&gt;
     fred.point.x = 10;&lt;br /&gt;
     fred.point.y = 15;&lt;br /&gt;
     &lt;br /&gt;
     // Other code uses PropertyCatalog&lt;br /&gt;
     EpicsPropertyCatalog *pc = &amp;amp;fred;&lt;br /&gt;
 &lt;br /&gt;
     // It can access known properties in chosen order&lt;br /&gt;
     EpicsProperty *p = pc-&amp;gt;findProperty(&amp;quot;value&amp;quot;);&lt;br /&gt;
     double d;&lt;br /&gt;
     if (p &amp;amp;&amp;amp; p-&amp;gt;getDouble(d))&lt;br /&gt;
         printf(&amp;quot;Value = %.2f\n&amp;quot;, d);&lt;br /&gt;
     else&lt;br /&gt;
         printf(&amp;quot;Cannot get value as double\n&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
     // Or inspect unknown property catalogs&lt;br /&gt;
     EpicsPropertyDumper::dump(pc);&lt;br /&gt;
     &lt;br /&gt;
     return 0;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Comparable DataAccess Example? ==&lt;br /&gt;
This is the result of my attempt to implement the TestRecord&lt;br /&gt;
and main() code based on the August 23 snapshot of DataAccess.&lt;br /&gt;
&lt;br /&gt;
 #include &amp;lt;iostream&amp;gt;&lt;br /&gt;
 #include &amp;lt;string&amp;gt;&lt;br /&gt;
 #include &amp;quot;dataAccess.h&amp;quot;&lt;br /&gt;
 #include &amp;quot;daPropertyId.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 using namespace da;&lt;br /&gt;
&lt;br /&gt;
 static const propertyId pi_value(&amp;quot;value&amp;quot;);&lt;br /&gt;
 static const propertyId pi_units(&amp;quot;units&amp;quot;);&lt;br /&gt;
 static const propertyId pi_point(&amp;quot;point&amp;quot;);&lt;br /&gt;
 static const propertyId pi_x(&amp;quot;x&amp;quot;);&lt;br /&gt;
 static const propertyId pi_y(&amp;quot;y&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
 class TestRecord : public propertyCatalog&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual ~TestRecord() {};&lt;br /&gt;
     // Unsure if database code with pointer to the record&lt;br /&gt;
     // should still directly access the data,&lt;br /&gt;
     // or if the data should be protected/private.&lt;br /&gt;
     double value;&lt;br /&gt;
     string units;&lt;br /&gt;
     struct&lt;br /&gt;
     {&lt;br /&gt;
         double x, y;&lt;br /&gt;
     }   point;&lt;br /&gt;
&lt;br /&gt;
     // propertyCatalog&lt;br /&gt;
     void traverse ( propertyViewer &amp;amp;v) const&lt;br /&gt;
     {&lt;br /&gt;
         v.reveal(pi_value, this-&amp;gt;value);&lt;br /&gt;
         // Don't know how to turn a std::string into a stringSegment&lt;br /&gt;
         // v.reveal(pi_units, this-&amp;gt;units); ??&lt;br /&gt;
         // Don't know how to reveal the 'point'&lt;br /&gt;
         // without changing the struct point itself&lt;br /&gt;
         // into a class derived from propertyCatalog&lt;br /&gt;
         v.reveal(pi_x,     this-&amp;gt;point.x);&lt;br /&gt;
         v.reveal(pi_y,     this-&amp;gt;point.y);&lt;br /&gt;
     }&lt;br /&gt;
     // Basically the same, supposedly no longer necessary with latest DA&lt;br /&gt;
     traverseModifyStatus traverse ( propertyManipulator &amp;amp; m)&lt;br /&gt;
     {&lt;br /&gt;
         m.reveal(pi_value, this-&amp;gt;value);&lt;br /&gt;
         // m.reveal(pi_units, this-&amp;gt;units); ??&lt;br /&gt;
         m.reveal(pi_x,     this-&amp;gt;point.x);&lt;br /&gt;
         m.reveal(pi_y,     this-&amp;gt;point.y);&lt;br /&gt;
         return tmsSuccess;&lt;br /&gt;
     }&lt;br /&gt;
     bool find ( const propertyId &amp;amp; id, propertyViewer &amp;amp; v) const&lt;br /&gt;
     {   // Naive, not using the 'locator' helper, but less code&lt;br /&gt;
         if (id == pi_value)&lt;br /&gt;
         {&lt;br /&gt;
             v.reveal(pi_value, this-&amp;gt;value);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         if (id == pi_x)&lt;br /&gt;
         {&lt;br /&gt;
             v.reveal(pi_value, this-&amp;gt;point.x);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         if (id == pi_y)&lt;br /&gt;
         {&lt;br /&gt;
             v.reveal(pi_value, this-&amp;gt;point.y);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
     }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 class Viewer : public propertyViewer&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
    void reveal ( const propertyId &amp;amp;id, const double &amp;amp;d, const propertyCatalog &amp;amp;x)&lt;br /&gt;
    {   // Don't know how to print the id.getName() stringSegment.&lt;br /&gt;
        cout &amp;lt;&amp;lt; &amp;quot;?? = &amp;quot; &amp;lt;&amp;lt; d &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    void reveal ( const propertyId &amp;amp;, const int &amp;amp;, const propertyCatalog &amp;amp;x)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
    void reveal ( const propertyId &amp;amp;, const unsigned int &amp;amp;, const propertyCatalog &amp;amp;x)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
    void reveal ( const propertyId &amp;amp;, const long &amp;amp;, const propertyCatalog &amp;amp;x)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
    void reveal ( const propertyId &amp;amp;, const unsigned long &amp;amp;, const propertyCatalog &amp;amp;x)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
    void reveal ( const propertyId &amp;amp;, const epicsTime &amp;amp;, const propertyCatalog &amp;amp;x)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
    void reveal ( const propertyId &amp;amp;, const stringSegment &amp;amp;, const propertyCatalog &amp;amp;x)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
    void reveal ( const propertyId &amp;amp;, const enumStateSet &amp;amp; )&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
    void reveal ( const propertyId &amp;amp;, const arraySegment &amp;amp;, const propertyCatalog &amp;amp;x)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
    void reveal ( const propertyId &amp;amp;, const propertyCatalog &amp;amp;x)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 int main ()&lt;br /&gt;
 {&lt;br /&gt;
    TestRecord fred;&lt;br /&gt;
     // Record/device support might have direct access to the fields:&lt;br /&gt;
     fred.value = 42.3;&lt;br /&gt;
     fred.units = &amp;quot;a.u.&amp;quot;;&lt;br /&gt;
     fred.point.x = 10;&lt;br /&gt;
     fred.point.y = 15;&lt;br /&gt;
&lt;br /&gt;
     // Assume other code that uses PropertyCatalog&lt;br /&gt;
     // without knowing about TestRecord&lt;br /&gt;
     propertyCatalog *pc = &amp;amp;fred;&lt;br /&gt;
     // It can access known properties in chosen order&lt;br /&gt;
     // Unclear how to get the 'value' as a 'double'.&lt;br /&gt;
     // This will 'reveal' whatever's there.&lt;br /&gt;
     // I assume one would have to implement a propertyCatalog&lt;br /&gt;
     // for target = 'double value' and then assign the given pc&lt;br /&gt;
     // to that target.&lt;br /&gt;
     Viewer dumper;&lt;br /&gt;
     pc-&amp;gt;find(pi_value, dumper);&lt;br /&gt;
     // Or inspect unknown property catalogs&lt;br /&gt;
     pc-&amp;gt;traverse(dumper);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Thoughts ==&lt;br /&gt;
DataAccess&lt;br /&gt;
 - does not provide type information&lt;br /&gt;
 + property ID handling already optimized&lt;br /&gt;
 + allows segmented strings and arrays&lt;br /&gt;
 - unclear how to interface a std::string or char [] string&lt;br /&gt;
 - cannot interface the example record without turning 'struct point' into its own class, derived from propertyCatalog?&lt;br /&gt;
 * might be more complete but doesn't all compile&lt;br /&gt;
&lt;br /&gt;
This Interface&lt;br /&gt;
 + does provide type information&lt;br /&gt;
 - property IDs only strings&lt;br /&gt;
 - requires contiguous C-String&lt;br /&gt;
 + supports C-string&lt;br /&gt;
 + can interface the struct point as is&lt;br /&gt;
 * is incomplete but compiles&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Data_Interface&amp;diff=503</id>
		<title>V4 Data Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Data_Interface&amp;diff=503"/>
		<updated>2005-08-09T13:05:08Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Class Layout Image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Issue ==&lt;br /&gt;
I think the following data interface is easier to understand&lt;br /&gt;
than the data access approach.&lt;br /&gt;
&lt;br /&gt;
Am I wrong, because Data Access is really much easier?&lt;br /&gt;
&lt;br /&gt;
Am I right, but the approach shown here will never work?&lt;br /&gt;
&lt;br /&gt;
Would it work, but performance would be unacceptable?&lt;br /&gt;
&lt;br /&gt;
[[Image:V4DataInter.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Data Interface Classes ===&lt;br /&gt;
 &lt;br /&gt;
 // Description of basic data character&lt;br /&gt;
 enum EpicsDataCharacter&lt;br /&gt;
 {&lt;br /&gt;
     Octet,     // Raw bits,known to a server/client pair, but no one else&lt;br /&gt;
     Bool,&lt;br /&gt;
     Integer,   // Discrete, available with 16, 32, 64 bits&lt;br /&gt;
     Real,      // Floating-point, available with 32 and 64 bits&lt;br /&gt;
     String,    // UTF-8&lt;br /&gt;
     Enum,      // &amp;gt;=0 Integer with state names&lt;br /&gt;
     Structure,&lt;br /&gt;
     TimeStamp  // For efficiency?&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // Full description of the data&lt;br /&gt;
 class EpicsDataDescriptor&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual EpicsDataCharacter getType() = 0;&lt;br /&gt;
     virtual int  getNumDims() = 0;      // Scalar, vector: 1, matrix: &amp;gt;1&lt;br /&gt;
     virtual int  getSize(int dim) = 0;  // Scalar: 1&lt;br /&gt;
     virtual int  getBitsize() = 0;      // bits of integer or real. 0 for rest?&lt;br /&gt;
     virtual bool isSigned() = 0;        // Integer might be unsigned?&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // When the exact size-locked types are needed,&lt;br /&gt;
 // use these integer types from stdint.h:&lt;br /&gt;
 //     int8_t, int16_t, int32_t, int64_t&lt;br /&gt;
 // and the following:&lt;br /&gt;
 typedef bool          bool_t;&lt;br /&gt;
 typedef float         real32_t;&lt;br /&gt;
 typedef double        real64_t;&lt;br /&gt;
&lt;br /&gt;
 // Read-Access to the data.&lt;br /&gt;
 // This one tries to allow as many conversions as possible:&lt;br /&gt;
 // Given an underlying Real, 64bit, scalar,&lt;br /&gt;
 // one can ask for a real64 but also int, string, ...&lt;br /&gt;
 //&lt;br /&gt;
 // The alternative would be individual reader interfaces&lt;br /&gt;
 // for numerics, strings, structures, numeric arrays, string arrays, ...&lt;br /&gt;
 // or even separate ones for int8, int16, ...&lt;br /&gt;
 // (Marty's &amp;quot;network accessable data&amp;quot; email to core talk did the latter).&lt;br /&gt;
 class EpicsDataReader : public EpicsDataDescriptor&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     // Get data as type XX as best as possible.&lt;br /&gt;
     // Example: getDouble returns true for&lt;br /&gt;
     //          Bool (d=0.0, 1.0), Integer, Real, Enum.&lt;br /&gt;
     virtual bool getReal64(real64_t &amp;amp;r) = 0;&lt;br /&gt;
     virtual bool getDouble(double &amp;amp;d) = 0;&lt;br /&gt;
     virtual bool getInt(int &amp;amp;d) = 0;&lt;br /&gt;
     // String&lt;br /&gt;
     virtual bool getString(char *buf, int buf_len) = 0;&lt;br /&gt;
     virtual int  getStringLength() = 0;&lt;br /&gt;
     virtual bool getCstring(const char *&amp;amp;c_ptr) = 0;&lt;br /&gt;
     /// many more, at least one for each supported type.&lt;br /&gt;
&lt;br /&gt;
     // A structure is accessed via a (sub-)catalog.&lt;br /&gt;
     // See below for PropertyCatalog.&lt;br /&gt;
     virtual class EpicsPropertyCatalog *getStructure() = 0;&lt;br /&gt;
     &lt;br /&gt;
     // Get array element. getDouble(x) == getArrayDouble(0, x)&lt;br /&gt;
     virtual bool getArrayDouble(int element, double &amp;amp;d) = 0;&lt;br /&gt;
     virtual bool getMatrixDouble(int dim, int element, double &amp;amp;d) = 0;&lt;br /&gt;
     virtual class EpicsPropertyCatalog *getArrayStructure(int element) = 0;&lt;br /&gt;
     /// For arrays, it could include per-element access&lt;br /&gt;
     /// as well as accessors that copy sub-arrays into client buffers.&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // &amp;quot;Property&amp;quot; has a name and data reader interface&lt;br /&gt;
 class EpicsProperty : public EpicsDataReader&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual const char *getName() = 0;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // A catalog of properties&lt;br /&gt;
 class EpicsPropertyCatalog&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual int            getNumProperties() = 0;&lt;br /&gt;
     virtual EpicsProperty *findProperty(const char *name) = 0;&lt;br /&gt;
     virtual EpicsProperty *getProperty(int index) = 0;&lt;br /&gt;
     /// Could also support name &amp;lt;-&amp;gt; ID conversions and&lt;br /&gt;
     /// access via numeric ID.&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
=== C++ Implementation ===&lt;br /&gt;
 // On the first read, skip to the 'Usage Example' below.&lt;br /&gt;
 // This code isn't the greatest, but simply here to get&lt;br /&gt;
 // example code that actually compiles &amp;amp; runs.&lt;br /&gt;
 #include &amp;lt;string&amp;gt;&lt;br /&gt;
 using std::string;&lt;br /&gt;
 #include &amp;lt;string.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Base class for the following EpicsProperty implementations.&lt;br /&gt;
 // Provides default handlers for everything.&lt;br /&gt;
 class EpicsPropertyBase : public EpicsProperty&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsPropertyBase(const char *name)&lt;br /&gt;
     {   this-&amp;gt;name = name; }&lt;br /&gt;
&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     // defaults for scalar&lt;br /&gt;
     int getNumDims()&lt;br /&gt;
     {   return 1; }&lt;br /&gt;
     int getSize(int dim)&lt;br /&gt;
     {   return dim==0 ? 1 : 0; }&lt;br /&gt;
     int getBitsize()&lt;br /&gt;
     {   return 0; }&lt;br /&gt;
     bool isSigned()&lt;br /&gt;
     {   return true; }&lt;br /&gt;
&lt;br /&gt;
     // DataReader&lt;br /&gt;
     bool getReal64(real64_t &amp;amp;r)&lt;br /&gt;
     {   return getDouble(r); }&lt;br /&gt;
     bool getDouble(double &amp;amp;d)&lt;br /&gt;
     {&lt;br /&gt;
         int i;&lt;br /&gt;
         if (!getInt(i))&lt;br /&gt;
             return false;&lt;br /&gt;
         d = i;&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     bool getInt(int &amp;amp;i)&lt;br /&gt;
     {&lt;br /&gt;
         double d;&lt;br /&gt;
         if (!getDouble(d))&lt;br /&gt;
             return false;&lt;br /&gt;
         i = (int)d;&lt;br /&gt;
         return true;&lt;br /&gt;
     }    &lt;br /&gt;
     bool getString(char *buf, int buf_len)&lt;br /&gt;
     {&lt;br /&gt;
         const char *ptr;&lt;br /&gt;
         if (!getCstring(ptr))&lt;br /&gt;
             return false;&lt;br /&gt;
         strncpy(buf, ptr, buf_len-1);&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     int  getStringLength()&lt;br /&gt;
     {   return 0; }&lt;br /&gt;
     bool getCstring(const char *&amp;amp;c_ptr)&lt;br /&gt;
     {   return false; }&lt;br /&gt;
     EpicsPropertyCatalog *getStructure()&lt;br /&gt;
     {   return 0; }&lt;br /&gt;
     bool getArrayDouble(int element, double &amp;amp;d)&lt;br /&gt;
     {   return (element == 0) ? getDouble(d) : false; }&lt;br /&gt;
     bool getMatrixDouble(int dim, int element, double &amp;amp;d)&lt;br /&gt;
     {   return (dim == 0) ? getArrayDouble(element, d) : false; }&lt;br /&gt;
     EpicsPropertyCatalog *getArrayStructure(int element)&lt;br /&gt;
     {   return element==0 ? getStructure() : 0; }&lt;br /&gt;
&lt;br /&gt;
     // EpicsProperty&lt;br /&gt;
     const char *getName()&lt;br /&gt;
     {   return name.c_str(); }&lt;br /&gt;
 private:&lt;br /&gt;
        string name;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // EpicsProperty for 'double'. Actual data is outside,&lt;br /&gt;
 // the EpicsDoubleProperty keeps a pointer to it.&lt;br /&gt;
 class EpicsDoubleProperty : public EpicsPropertyBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsDoubleProperty(const char *name, const double *p)&lt;br /&gt;
             : EpicsPropertyBase(name)&lt;br /&gt;
     {   this-&amp;gt;p = p; }&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     EpicsDataCharacter getType()&lt;br /&gt;
     {   return Real; }&lt;br /&gt;
     int getBitsize()&lt;br /&gt;
     {   return sizeof(double)*8; }&lt;br /&gt;
     // EpicsDataReader&lt;br /&gt;
     bool getDouble(double &amp;amp;d)&lt;br /&gt;
     {   d = *p; }&lt;br /&gt;
 private:&lt;br /&gt;
     const double *p;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 // Handler for string. Data is not owned by this class.&lt;br /&gt;
 class EpicsStringProperty : public EpicsPropertyBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsStringProperty(const char *name, const string *p)&lt;br /&gt;
             : EpicsPropertyBase(name)&lt;br /&gt;
     {   this-&amp;gt;p = p; }&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     EpicsDataCharacter getType()&lt;br /&gt;
     {   return String; }&lt;br /&gt;
     // EpicsDataReader&lt;br /&gt;
     int  getStringLength()&lt;br /&gt;
     {   return p-&amp;gt;length(); }&lt;br /&gt;
     virtual bool getCstring(const char *&amp;amp;c_ptr)&lt;br /&gt;
     {&lt;br /&gt;
         c_ptr = p-&amp;gt;c_str();&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     const string *p;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 class EpicsPropertyCatalogBase : public EpicsPropertyCatalog&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsPropertyCatalogBase(int num_properties, EpicsProperty **properties)&lt;br /&gt;
             : num_properties(num_properties), properties(properties)&lt;br /&gt;
     {}&lt;br /&gt;
     // EpicsPropertyCatalog&lt;br /&gt;
     int getNumProperties()&lt;br /&gt;
     {   return num_properties; }&lt;br /&gt;
     EpicsProperty *findProperty(const char *name)&lt;br /&gt;
     {&lt;br /&gt;
         int i;&lt;br /&gt;
         for (i=0; i&amp;lt;num_properties; ++i)&lt;br /&gt;
             if (!strcmp(properties[i]-&amp;gt;getName(), name))&lt;br /&gt;
                 return properties[i];&lt;br /&gt;
         return 0;&lt;br /&gt;
     }&lt;br /&gt;
     EpicsProperty *getProperty(int index)&lt;br /&gt;
     {&lt;br /&gt;
         return (index &amp;gt;=0  &amp;amp;&amp;amp;  index &amp;lt; num_properties) ? properties[index] : 0;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     int            num_properties;&lt;br /&gt;
     EpicsProperty **properties;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 // Handler for struct. Keeps pointers to EpicsProperty&lt;br /&gt;
 // interfaces for the struct's fields.&lt;br /&gt;
 class EpicsStructureProperty : public EpicsPropertyBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsStructureProperty(const char *name, int num_properties, EpicsProperty **properties)&lt;br /&gt;
             : EpicsPropertyBase(name), pc(num_properties, properties)&lt;br /&gt;
     {}&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     EpicsDataCharacter getType()&lt;br /&gt;
     {   return Structure; }&lt;br /&gt;
     // EpicsDataReader&lt;br /&gt;
     EpicsPropertyCatalog *getStructure()&lt;br /&gt;
     {   return &amp;amp;pc; }&lt;br /&gt;
 private:&lt;br /&gt;
     EpicsPropertyCatalogBase pc;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
=== Example for data known at compile-time ===&lt;br /&gt;
 // This is the code that provides a property catalog.&lt;br /&gt;
 // Again skip on the first read.&lt;br /&gt;
 //&lt;br /&gt;
 // All of this could be generated from DBD information.&lt;br /&gt;
 class TestRecord : public EpicsPropertyCatalogBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     TestRecord();&lt;br /&gt;
     // Unsure if database code with pointer to the record&lt;br /&gt;
     // should still directly access the data,&lt;br /&gt;
     // or if the data should be protected/private.&lt;br /&gt;
     double value;&lt;br /&gt;
     string units;&lt;br /&gt;
     struct&lt;br /&gt;
     {&lt;br /&gt;
         double x, y;&lt;br /&gt;
     }   point;&lt;br /&gt;
  private:&lt;br /&gt;
     EpicsProperty *properties[3];&lt;br /&gt;
     EpicsProperty *point_properties[2];&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 TestRecord::TestRecord() &lt;br /&gt;
         : EpicsPropertyCatalogBase(3, properties)&lt;br /&gt;
 {&lt;br /&gt;
     point_properties[0] = new EpicsDoubleProperty(&amp;quot;x&amp;quot;, &amp;amp;point.x);&lt;br /&gt;
     point_properties[1] = new EpicsDoubleProperty(&amp;quot;y&amp;quot;, &amp;amp;point.y);&lt;br /&gt;
     properties[0] = new EpicsDoubleProperty(&amp;quot;value&amp;quot;, &amp;amp;value);&lt;br /&gt;
     properties[1] = new EpicsStringProperty(&amp;quot;units&amp;quot;, &amp;amp;units);&lt;br /&gt;
     properties[2] = new EpicsStructureProperty(&amp;quot;point&amp;quot;, 2, point_properties);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Example Generic 'Dumper' ===&lt;br /&gt;
&lt;br /&gt;
 class EpicsPropertyDumper&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     static const char *type2string(EpicsDataCharacter c)&lt;br /&gt;
     {&lt;br /&gt;
         const char *strings[] =&lt;br /&gt;
         {&lt;br /&gt;
             &amp;quot;Octet&amp;quot;,&lt;br /&gt;
             &amp;quot;Bool&amp;quot;,&lt;br /&gt;
             &amp;quot;Integer&amp;quot;,&lt;br /&gt;
             &amp;quot;Real&amp;quot;,&lt;br /&gt;
             &amp;quot;String&amp;quot;,&lt;br /&gt;
             &amp;quot;Enum&amp;quot;,&lt;br /&gt;
             &amp;quot;Structure&amp;quot;,&lt;br /&gt;
             &amp;quot;TimeStamp&amp;quot;&lt;br /&gt;
         };&lt;br /&gt;
         &lt;br /&gt;
         if (c &amp;lt; 0  ||  c &amp;gt;= sizeof(strings)/sizeof(const char *))&lt;br /&gt;
             return &amp;quot;&amp;lt;unknown&amp;gt;&amp;quot;;&lt;br /&gt;
         return strings[c];&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     static void dump(EpicsPropertyCatalog *pc)&lt;br /&gt;
     {   dump(0, pc); }&lt;br /&gt;
 private:&lt;br /&gt;
     static void indent(int level)&lt;br /&gt;
     {   for (int i=0; i&amp;lt;level; ++i)  printf(&amp;quot;    &amp;quot;); }&lt;br /&gt;
     static void dump(int level, EpicsPropertyCatalog *pc)&lt;br /&gt;
     {&lt;br /&gt;
         double d;&lt;br /&gt;
         int i;&lt;br /&gt;
         for (i=0; i&amp;lt;pc-&amp;gt;getNumProperties(); ++i)&lt;br /&gt;
         { &lt;br /&gt;
            indent(level);&lt;br /&gt;
            EpicsProperty *p = pc-&amp;gt;getProperty(i);&lt;br /&gt;
            EpicsDataCharacter type = p-&amp;gt;getType();&lt;br /&gt;
            printf(&amp;quot;%s (%s, %d %s, %d bits) = &amp;quot;,&lt;br /&gt;
                   p-&amp;gt;getName(), type2string(type),&lt;br /&gt;
                   p-&amp;gt;getSize(0),&lt;br /&gt;
                   (p-&amp;gt;getSize(0) == 1 ? &amp;quot;element&amp;quot; : &amp;quot;elements&amp;quot;),&lt;br /&gt;
                   p-&amp;gt;getBitsize());&lt;br /&gt;
            switch (type)&lt;br /&gt;
            {&lt;br /&gt;
            case String:&lt;br /&gt;
                const char *s;&lt;br /&gt;
                if (p-&amp;gt;getCstring(s))&lt;br /&gt;
                    printf(&amp;quot;'%s' (%d chars)\n&amp;quot;, s, p-&amp;gt;getStringLength());&lt;br /&gt;
                else&lt;br /&gt;
                    printf(&amp;quot;&amp;lt;cannot get string&amp;gt;\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            case Bool:&lt;br /&gt;
            case Integer:&lt;br /&gt;
            case Real:&lt;br /&gt;
                double d;&lt;br /&gt;
                if (p-&amp;gt;getDouble(d))&lt;br /&gt;
                    printf(&amp;quot;%.2f\n&amp;quot;, d);&lt;br /&gt;
                else&lt;br /&gt;
                    printf(&amp;quot;&amp;lt;cannot get double&amp;gt;\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            case Structure:&lt;br /&gt;
                indent(level);&lt;br /&gt;
                printf(&amp;quot;\n{\n&amp;quot;);&lt;br /&gt;
                dump(level+1, p-&amp;gt;getStructure());&lt;br /&gt;
                indent(level);&lt;br /&gt;
                printf(&amp;quot;}\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            default:&lt;br /&gt;
                printf(&amp;quot;&amp;lt;unhandled type %d&amp;gt;\n&amp;quot;, p-&amp;gt;getType());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 }; &lt;br /&gt;
 &lt;br /&gt;
=== Usage Example ===&lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 int main()&lt;br /&gt;
 {&lt;br /&gt;
     TestRecord fred;&lt;br /&gt;
     // Record/device support might have direct access to the fields:&lt;br /&gt;
     fred.value = 42.3;&lt;br /&gt;
     fred.units = &amp;quot;a.u.&amp;quot;;&lt;br /&gt;
     fred.point.x = 10;&lt;br /&gt;
     fred.point.y = 15;&lt;br /&gt;
     &lt;br /&gt;
     // Other code uses PropertyCatalog&lt;br /&gt;
     EpicsPropertyCatalog *pc = &amp;amp;fred;&lt;br /&gt;
 &lt;br /&gt;
     // It can access known properties in chosen order&lt;br /&gt;
     EpicsProperty *p = pc-&amp;gt;findProperty(&amp;quot;value&amp;quot;);&lt;br /&gt;
     double d;&lt;br /&gt;
     if (p &amp;amp;&amp;amp; p-&amp;gt;getDouble(d))&lt;br /&gt;
         printf(&amp;quot;Value = %.2f\n&amp;quot;, d);&lt;br /&gt;
     else&lt;br /&gt;
         printf(&amp;quot;Cannot get value as double\n&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
     // Or inspect unknown property catalogs&lt;br /&gt;
     EpicsPropertyDumper::dump(pc);&lt;br /&gt;
     &lt;br /&gt;
     return 0;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The output of this test:&lt;br /&gt;
 Value = 42.30&lt;br /&gt;
 value (Real, 1 element, 64 bits) = 42.30&lt;br /&gt;
 units (String, 1 element, 0 bits) = 'a.u.' (4 chars)&lt;br /&gt;
 point (Structure, 1 element, 0 bits) = &lt;br /&gt;
 {&lt;br /&gt;
    x (Real, 1 element, 64 bits) = 10.00&lt;br /&gt;
    y (Real, 1 element, 64 bits) = 15.00&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=File:V4DataInter.jpg&amp;diff=2763</id>
		<title>File:V4DataInter.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=File:V4DataInter.jpg&amp;diff=2763"/>
		<updated>2005-08-09T13:03:58Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Data Interface Layout&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Data Interface Layout&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Data_Interface&amp;diff=453</id>
		<title>V4 Data Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Data_Interface&amp;diff=453"/>
		<updated>2005-08-04T20:21:13Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: added structure handling to example&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Issue ==&lt;br /&gt;
I think the following data interface is easier to understand&lt;br /&gt;
than the data access approach.&lt;br /&gt;
&lt;br /&gt;
Am I wrong, because Data Access is really much easier?&lt;br /&gt;
&lt;br /&gt;
Am I right, but the approach shown here will never work?&lt;br /&gt;
&lt;br /&gt;
Would it work, but performance would be unacceptable?&lt;br /&gt;
&lt;br /&gt;
=== Data Interface Classes ===&lt;br /&gt;
 &lt;br /&gt;
 // Description of basic data character&lt;br /&gt;
 enum EpicsDataCharacter&lt;br /&gt;
 {&lt;br /&gt;
     Octet,     // Raw bits,known to a server/client pair, but no one else&lt;br /&gt;
     Bool,&lt;br /&gt;
     Integer,   // Discrete, available with 16, 32, 64 bits&lt;br /&gt;
     Real,      // Floating-point, available with 32 and 64 bits&lt;br /&gt;
     String,    // UTF-8&lt;br /&gt;
     Enum,      // &amp;gt;=0 Integer with state names&lt;br /&gt;
     Structure,&lt;br /&gt;
     TimeStamp  // For efficiency?&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // Full description of the data&lt;br /&gt;
 class EpicsDataDescriptor&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual EpicsDataCharacter getType() = 0;&lt;br /&gt;
     virtual int  getNumDims() = 0;      // Scalar, vector: 1, matrix: &amp;gt;1&lt;br /&gt;
     virtual int  getSize(int dim) = 0;  // Scalar: 1&lt;br /&gt;
     virtual int  getBitsize() = 0;      // bits of integer or real. 0 for rest?&lt;br /&gt;
     virtual bool isSigned() = 0;        // Integer might be unsigned?&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // When the exact size-locked types are needed,&lt;br /&gt;
 // use these integer types from stdint.h:&lt;br /&gt;
 //     int8_t, int16_t, int32_t, int64_t&lt;br /&gt;
 // and the following:&lt;br /&gt;
 typedef bool          bool_t;&lt;br /&gt;
 typedef float         real32_t;&lt;br /&gt;
 typedef double        real64_t;&lt;br /&gt;
&lt;br /&gt;
 // Read-Access to the data.&lt;br /&gt;
 // This one tries to allow as many conversions as possible:&lt;br /&gt;
 // Given an underlying Real, 64bit, scalar,&lt;br /&gt;
 // one can ask for a real64 but also int, string, ...&lt;br /&gt;
 //&lt;br /&gt;
 // The alternative would be individual reader interfaces&lt;br /&gt;
 // for numerics, strings, structures, numeric arrays, string arrays, ...&lt;br /&gt;
 // or even separate ones for int8, int16, ...&lt;br /&gt;
 // (Marty's &amp;quot;network accessable data&amp;quot; email to core talk did the latter).&lt;br /&gt;
 class EpicsDataReader : public EpicsDataDescriptor&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     // Get data as type XX as best as possible.&lt;br /&gt;
     // Example: getDouble returns true for&lt;br /&gt;
     //          Bool (d=0.0, 1.0), Integer, Real, Enum.&lt;br /&gt;
     virtual bool getReal64(real64_t &amp;amp;r) = 0;&lt;br /&gt;
     virtual bool getDouble(double &amp;amp;d) = 0;&lt;br /&gt;
     virtual bool getInt(int &amp;amp;d) = 0;&lt;br /&gt;
     // String&lt;br /&gt;
     virtual bool getString(char *buf, int buf_len) = 0;&lt;br /&gt;
     virtual int  getStringLength() = 0;&lt;br /&gt;
     virtual bool getCstring(const char *&amp;amp;c_ptr) = 0;&lt;br /&gt;
     /// many more, at least one for each supported type.&lt;br /&gt;
&lt;br /&gt;
     // A structure is accessed via a (sub-)catalog.&lt;br /&gt;
     // See below for PropertyCatalog.&lt;br /&gt;
     virtual class EpicsPropertyCatalog *getStructure() = 0;&lt;br /&gt;
     &lt;br /&gt;
     // Get array element. getDouble(x) == getArrayDouble(0, x)&lt;br /&gt;
     virtual bool getArrayDouble(int element, double &amp;amp;d) = 0;&lt;br /&gt;
     virtual bool getMatrixDouble(int dim, int element, double &amp;amp;d) = 0;&lt;br /&gt;
     virtual class EpicsPropertyCatalog *getArrayStructure(int element) = 0;&lt;br /&gt;
     /// For arrays, it could include per-element access&lt;br /&gt;
     /// as well as accessors that copy sub-arrays into client buffers.&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // &amp;quot;Property&amp;quot; has a name and data reader interface&lt;br /&gt;
 class EpicsProperty : public EpicsDataReader&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual const char *getName() = 0;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // A catalog of properties&lt;br /&gt;
 class EpicsPropertyCatalog&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual int            getNumProperties() = 0;&lt;br /&gt;
     virtual EpicsProperty *findProperty(const char *name) = 0;&lt;br /&gt;
     virtual EpicsProperty *getProperty(int index) = 0;&lt;br /&gt;
     /// Could also support name &amp;lt;-&amp;gt; ID conversions and&lt;br /&gt;
     /// access via numeric ID.&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
=== C++ Implementation ===&lt;br /&gt;
 // On the first read, skip to the 'Usage Example' below.&lt;br /&gt;
 // This code isn't the greatest, but simply here to get&lt;br /&gt;
 // example code that actually compiles &amp;amp; runs.&lt;br /&gt;
 #include &amp;lt;string&amp;gt;&lt;br /&gt;
 using std::string;&lt;br /&gt;
 #include &amp;lt;string.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 // Base class for the following EpicsProperty implementations.&lt;br /&gt;
 // Provides default handlers for everything.&lt;br /&gt;
 class EpicsPropertyBase : public EpicsProperty&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsPropertyBase(const char *name)&lt;br /&gt;
     {   this-&amp;gt;name = name; }&lt;br /&gt;
&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     // defaults for scalar&lt;br /&gt;
     int getNumDims()&lt;br /&gt;
     {   return 1; }&lt;br /&gt;
     int getSize(int dim)&lt;br /&gt;
     {   return dim==0 ? 1 : 0; }&lt;br /&gt;
     int getBitsize()&lt;br /&gt;
     {   return 0; }&lt;br /&gt;
     bool isSigned()&lt;br /&gt;
     {   return true; }&lt;br /&gt;
&lt;br /&gt;
     // DataReader&lt;br /&gt;
     bool getReal64(real64_t &amp;amp;r)&lt;br /&gt;
     {   return getDouble(r); }&lt;br /&gt;
     bool getDouble(double &amp;amp;d)&lt;br /&gt;
     {&lt;br /&gt;
         int i;&lt;br /&gt;
         if (!getInt(i))&lt;br /&gt;
             return false;&lt;br /&gt;
         d = i;&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     bool getInt(int &amp;amp;i)&lt;br /&gt;
     {&lt;br /&gt;
         double d;&lt;br /&gt;
         if (!getDouble(d))&lt;br /&gt;
             return false;&lt;br /&gt;
         i = (int)d;&lt;br /&gt;
         return true;&lt;br /&gt;
     }    &lt;br /&gt;
     bool getString(char *buf, int buf_len)&lt;br /&gt;
     {&lt;br /&gt;
         const char *ptr;&lt;br /&gt;
         if (!getCstring(ptr))&lt;br /&gt;
             return false;&lt;br /&gt;
         strncpy(buf, ptr, buf_len-1);&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
     int  getStringLength()&lt;br /&gt;
     {   return 0; }&lt;br /&gt;
     bool getCstring(const char *&amp;amp;c_ptr)&lt;br /&gt;
     {   return false; }&lt;br /&gt;
     EpicsPropertyCatalog *getStructure()&lt;br /&gt;
     {   return 0; }&lt;br /&gt;
     bool getArrayDouble(int element, double &amp;amp;d)&lt;br /&gt;
     {   return (element == 0) ? getDouble(d) : false; }&lt;br /&gt;
     bool getMatrixDouble(int dim, int element, double &amp;amp;d)&lt;br /&gt;
     {   return (dim == 0) ? getArrayDouble(element, d) : false; }&lt;br /&gt;
     EpicsPropertyCatalog *getArrayStructure(int element)&lt;br /&gt;
     {   return element==0 ? getStructure() : 0; }&lt;br /&gt;
&lt;br /&gt;
     // EpicsProperty&lt;br /&gt;
     const char *getName()&lt;br /&gt;
     {   return name.c_str(); }&lt;br /&gt;
 private:&lt;br /&gt;
        string name;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // EpicsProperty for 'double'. Actual data is outside,&lt;br /&gt;
 // the EpicsDoubleProperty keeps a pointer to it.&lt;br /&gt;
 class EpicsDoubleProperty : public EpicsPropertyBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsDoubleProperty(const char *name, const double *p)&lt;br /&gt;
             : EpicsPropertyBase(name)&lt;br /&gt;
     {   this-&amp;gt;p = p; }&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     EpicsDataCharacter getType()&lt;br /&gt;
     {   return Real; }&lt;br /&gt;
     int getBitsize()&lt;br /&gt;
     {   return sizeof(double)*8; }&lt;br /&gt;
     // EpicsDataReader&lt;br /&gt;
     bool getDouble(double &amp;amp;d)&lt;br /&gt;
     {   d = *p; }&lt;br /&gt;
 private:&lt;br /&gt;
     const double *p;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 // Handler for string. Data is not owned by this class.&lt;br /&gt;
 class EpicsStringProperty : public EpicsPropertyBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsStringProperty(const char *name, const string *p)&lt;br /&gt;
             : EpicsPropertyBase(name)&lt;br /&gt;
     {   this-&amp;gt;p = p; }&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     EpicsDataCharacter getType()&lt;br /&gt;
     {   return String; }&lt;br /&gt;
     // EpicsDataReader&lt;br /&gt;
     int  getStringLength()&lt;br /&gt;
     {   return p-&amp;gt;length(); }&lt;br /&gt;
     virtual bool getCstring(const char *&amp;amp;c_ptr)&lt;br /&gt;
     {&lt;br /&gt;
         c_ptr = p-&amp;gt;c_str();&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     const string *p;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 class EpicsPropertyCatalogBase : public EpicsPropertyCatalog&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsPropertyCatalogBase(int num_properties, EpicsProperty **properties)&lt;br /&gt;
             : num_properties(num_properties), properties(properties)&lt;br /&gt;
     {}&lt;br /&gt;
     // EpicsPropertyCatalog&lt;br /&gt;
     int getNumProperties()&lt;br /&gt;
     {   return num_properties; }&lt;br /&gt;
     EpicsProperty *findProperty(const char *name)&lt;br /&gt;
     {&lt;br /&gt;
         int i;&lt;br /&gt;
         for (i=0; i&amp;lt;num_properties; ++i)&lt;br /&gt;
             if (!strcmp(properties[i]-&amp;gt;getName(), name))&lt;br /&gt;
                 return properties[i];&lt;br /&gt;
         return 0;&lt;br /&gt;
     }&lt;br /&gt;
     EpicsProperty *getProperty(int index)&lt;br /&gt;
     {&lt;br /&gt;
         return (index &amp;gt;=0  &amp;amp;&amp;amp;  index &amp;lt; num_properties) ? properties[index] : 0;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     int            num_properties;&lt;br /&gt;
     EpicsProperty **properties;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
 // Handler for struct. Keeps pointers to EpicsProperty&lt;br /&gt;
 // interfaces for the struct's fields.&lt;br /&gt;
 class EpicsStructureProperty : public EpicsPropertyBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsStructureProperty(const char *name, int num_properties, EpicsProperty **properties)&lt;br /&gt;
             : EpicsPropertyBase(name), pc(num_properties, properties)&lt;br /&gt;
     {}&lt;br /&gt;
     // EpicsDataDescriptor&lt;br /&gt;
     EpicsDataCharacter getType()&lt;br /&gt;
     {   return Structure; }&lt;br /&gt;
     // EpicsDataReader&lt;br /&gt;
     EpicsPropertyCatalog *getStructure()&lt;br /&gt;
     {   return &amp;amp;pc; }&lt;br /&gt;
 private:&lt;br /&gt;
     EpicsPropertyCatalogBase pc;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
=== Example for data known at compile-time ===&lt;br /&gt;
 // This is the code that provides a property catalog.&lt;br /&gt;
 // Again skip on the first read.&lt;br /&gt;
 //&lt;br /&gt;
 // All of this could be generated from DBD information.&lt;br /&gt;
 class TestRecord : public EpicsPropertyCatalogBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     TestRecord();&lt;br /&gt;
     // Unsure if database code with pointer to the record&lt;br /&gt;
     // should still directly access the data,&lt;br /&gt;
     // or if the data should be protected/private.&lt;br /&gt;
     double value;&lt;br /&gt;
     string units;&lt;br /&gt;
     struct&lt;br /&gt;
     {&lt;br /&gt;
         double x, y;&lt;br /&gt;
     }   point;&lt;br /&gt;
  private:&lt;br /&gt;
     EpicsProperty *properties[3];&lt;br /&gt;
     EpicsProperty *point_properties[2];&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 TestRecord::TestRecord() &lt;br /&gt;
         : EpicsPropertyCatalogBase(3, properties)&lt;br /&gt;
 {&lt;br /&gt;
     point_properties[0] = new EpicsDoubleProperty(&amp;quot;x&amp;quot;, &amp;amp;point.x);&lt;br /&gt;
     point_properties[1] = new EpicsDoubleProperty(&amp;quot;y&amp;quot;, &amp;amp;point.y);&lt;br /&gt;
     properties[0] = new EpicsDoubleProperty(&amp;quot;value&amp;quot;, &amp;amp;value);&lt;br /&gt;
     properties[1] = new EpicsStringProperty(&amp;quot;units&amp;quot;, &amp;amp;units);&lt;br /&gt;
     properties[2] = new EpicsStructureProperty(&amp;quot;point&amp;quot;, 2, point_properties);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Example Generic 'Dumper' ===&lt;br /&gt;
&lt;br /&gt;
 class EpicsPropertyDumper&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     static const char *type2string(EpicsDataCharacter c)&lt;br /&gt;
     {&lt;br /&gt;
         const char *strings[] =&lt;br /&gt;
         {&lt;br /&gt;
             &amp;quot;Octet&amp;quot;,&lt;br /&gt;
             &amp;quot;Bool&amp;quot;,&lt;br /&gt;
             &amp;quot;Integer&amp;quot;,&lt;br /&gt;
             &amp;quot;Real&amp;quot;,&lt;br /&gt;
             &amp;quot;String&amp;quot;,&lt;br /&gt;
             &amp;quot;Enum&amp;quot;,&lt;br /&gt;
             &amp;quot;Structure&amp;quot;,&lt;br /&gt;
             &amp;quot;TimeStamp&amp;quot;&lt;br /&gt;
         };&lt;br /&gt;
         &lt;br /&gt;
         if (c &amp;lt; 0  ||  c &amp;gt;= sizeof(strings)/sizeof(const char *))&lt;br /&gt;
             return &amp;quot;&amp;lt;unknown&amp;gt;&amp;quot;;&lt;br /&gt;
         return strings[c];&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     static void dump(EpicsPropertyCatalog *pc)&lt;br /&gt;
     {   dump(0, pc); }&lt;br /&gt;
 private:&lt;br /&gt;
     static void indent(int level)&lt;br /&gt;
     {   for (int i=0; i&amp;lt;level; ++i)  printf(&amp;quot;    &amp;quot;); }&lt;br /&gt;
     static void dump(int level, EpicsPropertyCatalog *pc)&lt;br /&gt;
     {&lt;br /&gt;
         double d;&lt;br /&gt;
         int i;&lt;br /&gt;
         for (i=0; i&amp;lt;pc-&amp;gt;getNumProperties(); ++i)&lt;br /&gt;
         { &lt;br /&gt;
            indent(level);&lt;br /&gt;
            EpicsProperty *p = pc-&amp;gt;getProperty(i);&lt;br /&gt;
            EpicsDataCharacter type = p-&amp;gt;getType();&lt;br /&gt;
            printf(&amp;quot;%s (%s, %d %s, %d bits) = &amp;quot;,&lt;br /&gt;
                   p-&amp;gt;getName(), type2string(type),&lt;br /&gt;
                   p-&amp;gt;getSize(0),&lt;br /&gt;
                   (p-&amp;gt;getSize(0) == 1 ? &amp;quot;element&amp;quot; : &amp;quot;elements&amp;quot;),&lt;br /&gt;
                   p-&amp;gt;getBitsize());&lt;br /&gt;
            switch (type)&lt;br /&gt;
            {&lt;br /&gt;
            case String:&lt;br /&gt;
                const char *s;&lt;br /&gt;
                if (p-&amp;gt;getCstring(s))&lt;br /&gt;
                    printf(&amp;quot;'%s' (%d chars)\n&amp;quot;, s, p-&amp;gt;getStringLength());&lt;br /&gt;
                else&lt;br /&gt;
                    printf(&amp;quot;&amp;lt;cannot get string&amp;gt;\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            case Bool:&lt;br /&gt;
            case Integer:&lt;br /&gt;
            case Real:&lt;br /&gt;
                double d;&lt;br /&gt;
                if (p-&amp;gt;getDouble(d))&lt;br /&gt;
                    printf(&amp;quot;%.2f\n&amp;quot;, d);&lt;br /&gt;
                else&lt;br /&gt;
                    printf(&amp;quot;&amp;lt;cannot get double&amp;gt;\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            case Structure:&lt;br /&gt;
                indent(level);&lt;br /&gt;
                printf(&amp;quot;\n{\n&amp;quot;);&lt;br /&gt;
                dump(level+1, p-&amp;gt;getStructure());&lt;br /&gt;
                indent(level);&lt;br /&gt;
                printf(&amp;quot;}\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            default:&lt;br /&gt;
                printf(&amp;quot;&amp;lt;unhandled type %d&amp;gt;\n&amp;quot;, p-&amp;gt;getType());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 }; &lt;br /&gt;
 &lt;br /&gt;
=== Usage Example ===&lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 int main()&lt;br /&gt;
 {&lt;br /&gt;
     TestRecord fred;&lt;br /&gt;
     // Record/device support might have direct access to the fields:&lt;br /&gt;
     fred.value = 42.3;&lt;br /&gt;
     fred.units = &amp;quot;a.u.&amp;quot;;&lt;br /&gt;
     fred.point.x = 10;&lt;br /&gt;
     fred.point.y = 15;&lt;br /&gt;
     &lt;br /&gt;
     // Other code uses PropertyCatalog&lt;br /&gt;
     EpicsPropertyCatalog *pc = &amp;amp;fred;&lt;br /&gt;
 &lt;br /&gt;
     // It can access known properties in chosen order&lt;br /&gt;
     EpicsProperty *p = pc-&amp;gt;findProperty(&amp;quot;value&amp;quot;);&lt;br /&gt;
     double d;&lt;br /&gt;
     if (p &amp;amp;&amp;amp; p-&amp;gt;getDouble(d))&lt;br /&gt;
         printf(&amp;quot;Value = %.2f\n&amp;quot;, d);&lt;br /&gt;
     else&lt;br /&gt;
         printf(&amp;quot;Cannot get value as double\n&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
     // Or inspect unknown property catalogs&lt;br /&gt;
     EpicsPropertyDumper::dump(pc);&lt;br /&gt;
     &lt;br /&gt;
     return 0;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The output of this test:&lt;br /&gt;
 Value = 42.30&lt;br /&gt;
 value (Real, 1 element, 64 bits) = 42.30&lt;br /&gt;
 units (String, 1 element, 0 bits) = 'a.u.' (4 chars)&lt;br /&gt;
 point (Structure, 1 element, 0 bits) = &lt;br /&gt;
 {&lt;br /&gt;
    x (Real, 1 element, 64 bits) = 10.00&lt;br /&gt;
    y (Real, 1 element, 64 bits) = 15.00&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Data_Interface&amp;diff=452</id>
		<title>V4 Data Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Data_Interface&amp;diff=452"/>
		<updated>2005-07-27T22:23:56Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Issue ==&lt;br /&gt;
I think the following data interface is easier to understand&lt;br /&gt;
than the data access approach.&lt;br /&gt;
&lt;br /&gt;
Am I wrong, because Data Access is really much easier?&lt;br /&gt;
&lt;br /&gt;
Am I right, but the approach shown here will never work?&lt;br /&gt;
&lt;br /&gt;
Would it work, but performance would be unacceptable?&lt;br /&gt;
&lt;br /&gt;
=== Data Interface Classes ===&lt;br /&gt;
 &lt;br /&gt;
 // Description of basic data character&lt;br /&gt;
 enum EpicsDataCharacter&lt;br /&gt;
 {&lt;br /&gt;
     Octet,     // Raw bits,known to a server/client pair, but no one else&lt;br /&gt;
     Bool,&lt;br /&gt;
     Integer,   // Discrete&lt;br /&gt;
     Real,      // Floating-point&lt;br /&gt;
     String,    // UTF-8&lt;br /&gt;
     Enum,      // &amp;gt;=0 Integer with state names&lt;br /&gt;
     Struct,&lt;br /&gt;
     TimeStamp  // For efficiency?&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // Full description of the data&lt;br /&gt;
 class EpicsDataDescriptor&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual EpicsDataCharacter getType() = 0;&lt;br /&gt;
     virtual int  getNumDims() = 0;      // Scalar, vector: 1, matrix: &amp;gt;1&lt;br /&gt;
     virtual int  getSize(int dim) = 0;  // Scalar: 1&lt;br /&gt;
     virtual int  getBitsize() = 0;      // bits of integer or real&lt;br /&gt;
     virtual bool isSigned() = 0;        // Integer might be unsigned&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // Access to the data. Two options:&lt;br /&gt;
 // 1) Via basic types of the language, contents will differ for Java, C++.&lt;br /&gt;
 //    (shown here)&lt;br /&gt;
 // 2) Locked types:&lt;br /&gt;
 //    C++ will probably define the full set&lt;br /&gt;
 //      int_8_t, int16_t, ...&lt;br /&gt;
 //    and Java can only handle a sub-set.&lt;br /&gt;
 class EpicsDataReader : public EpicsDataDescriptor&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     // Get data as type XX as best as possible.&lt;br /&gt;
     // Example: getDouble returns true for Bool (d=0.0, 1.0), Integer, Real, Enum.&lt;br /&gt;
     virtual bool getDouble(double &amp;amp;d) = 0;&lt;br /&gt;
     virtual bool getArrayDouble(int element, double &amp;amp;d) = 0;&lt;br /&gt;
     virtual bool getMatrixDouble(int dim, int element, double &amp;amp;d) = 0;&lt;br /&gt;
     virtual bool getInt(int &amp;amp;d) = 0;&lt;br /&gt;
     virtual bool getString(char *buf, int buf_len) = 0;&lt;br /&gt;
     virtual bool getCstring(const char *&amp;amp;c_ptr) = 0;&lt;br /&gt;
     /// many more, at least one for each supported type.&lt;br /&gt;
     /// For arrays, it could include per-element access&lt;br /&gt;
     /// as well as accessors that copy pieces into client buffers.&lt;br /&gt;
 };      &lt;br /&gt;
 &lt;br /&gt;
 // &amp;quot;Property&amp;quot; has a name and data reader interface&lt;br /&gt;
 class EpicsProperty : public EpicsDataReader&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual const char *getName() = 0;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // A catalog of properties&lt;br /&gt;
 class EpicsPropertyCatalog&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual int getNumProperties() = 0;&lt;br /&gt;
     virtual EpicsProperty *findProperty(const char *name) = 0;&lt;br /&gt;
     virtual EpicsProperty *getProperty(int index) = 0;&lt;br /&gt;
     /// Could also support name &amp;lt;-&amp;gt; ID conversions and&lt;br /&gt;
     /// access via numeric ID.&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
=== C++ Implementation ===&lt;br /&gt;
 // On the first read, skip to the 'Usage Example' below.&lt;br /&gt;
 // This code isn't the greatest, it simply here to get&lt;br /&gt;
 // example code that actually compiles &amp;amp; runs.&lt;br /&gt;
 #include &amp;lt;string&amp;gt;&lt;br /&gt;
 using std::string;&lt;br /&gt;
 #include &amp;lt;string.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 class EpicsPropertyBase : public EpicsProperty&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     EpicsPropertyBase(const char *name)&lt;br /&gt;
     {   this-&amp;gt;name = name; }&lt;br /&gt;
     // DataReader&lt;br /&gt;
     bool getDouble(double &amp;amp;d)&lt;br /&gt;
     {&lt;br /&gt;
         int i;&lt;br /&gt;
         if (getInt(i))&lt;br /&gt;
         {&lt;br /&gt;
             d = i;&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
     }&lt;br /&gt;
     bool getArrayDouble(int element, double &amp;amp;d)&lt;br /&gt;
     {   return (element == 0) ? getDouble(d) : false; }&lt;br /&gt;
     bool getMatrixDouble(int dim, int element, double &amp;amp;d)&lt;br /&gt;
     {   return (dim == 0) ? getArrayDouble(element, d) : false; }&lt;br /&gt;
     bool getInt(int &amp;amp;i)&lt;br /&gt;
     {&lt;br /&gt;
         double d;&lt;br /&gt;
         if (getDouble(d))&lt;br /&gt;
         {&lt;br /&gt;
             i = (int)d;&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
     }    &lt;br /&gt;
     bool getString(char *buf, int buf_len)&lt;br /&gt;
     {&lt;br /&gt;
         const char *ptr;&lt;br /&gt;
         if (getCstring(ptr))&lt;br /&gt;
         {&lt;br /&gt;
             strncpy(buf, ptr, buf_len-1);&lt;br /&gt;
             return true;&lt;br /&gt;
         }&lt;br /&gt;
         return false;&lt;br /&gt;
     }    &lt;br /&gt;
     bool getCstring(const char *&amp;amp;c_ptr)&lt;br /&gt;
     {&lt;br /&gt;
         return false;&lt;br /&gt;
     }    &lt;br /&gt;
     // EpicsProperty&lt;br /&gt;
        const char *getName()&lt;br /&gt;
        {   return name.c_str(); }&lt;br /&gt;
 private:&lt;br /&gt;
        string name;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 class EpicsDoubleProperty : public EpicsPropertyBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
        EpicsDoubleProperty(const char *name, const double *p)&lt;br /&gt;
             : EpicsPropertyBase(name)&lt;br /&gt;
     {   this-&amp;gt;p = p; }&lt;br /&gt;
        // EpicsDataDescriptor&lt;br /&gt;
        EpicsDataCharacter getType()&lt;br /&gt;
        {   return Real; }&lt;br /&gt;
     int getNumDims()&lt;br /&gt;
     {   return 1; }&lt;br /&gt;
     int getSize(int dim)&lt;br /&gt;
     {   return dim==1 ? 1 : 0; }&lt;br /&gt;
     int getBitsize()&lt;br /&gt;
     {   return sizeof(double)*8; }&lt;br /&gt;
     bool isSigned()&lt;br /&gt;
     {   return true; }&lt;br /&gt;
     // EpicsDataReader&lt;br /&gt;
     bool getDouble(double &amp;amp;d)&lt;br /&gt;
     {   d = *p; }&lt;br /&gt;
 private:&lt;br /&gt;
        const double *p;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 class EpicsStringProperty : public EpicsPropertyBase&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
        EpicsStringProperty(const char *name, const string *p)&lt;br /&gt;
             : EpicsPropertyBase(name)&lt;br /&gt;
     {   this-&amp;gt;p = p; }&lt;br /&gt;
        // EpicsDataDescriptor&lt;br /&gt;
        EpicsDataCharacter getType()&lt;br /&gt;
        {   return String; }&lt;br /&gt;
     int getNumDims()&lt;br /&gt;
     {   return 1; }&lt;br /&gt;
     int getSize(int dim)&lt;br /&gt;
     {   return dim==1 ? p-&amp;gt;length() : 0; }&lt;br /&gt;
     int getBitsize()&lt;br /&gt;
     {   return p-&amp;gt;length()*8; }&lt;br /&gt;
     bool isSigned()&lt;br /&gt;
     {   return false; }&lt;br /&gt;
     // EpicsDataReader&lt;br /&gt;
     virtual bool getCstring(const char *&amp;amp;c_ptr)&lt;br /&gt;
     {&lt;br /&gt;
         c_ptr = p-&amp;gt;c_str();&lt;br /&gt;
         return true;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     const string *p;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
=== Example for data known at compile-time ===&lt;br /&gt;
 // This is the code that provides a property catalog.&lt;br /&gt;
 // Again skip on the first read.&lt;br /&gt;
 //&lt;br /&gt;
 // All of this could be generated from DBD&lt;br /&gt;
 class AiRecord : public EpicsPropertyCatalog&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     AiRecord();&lt;br /&gt;
     // Unsure if database code with pointer to the record&lt;br /&gt;
     // should still directly access the data,&lt;br /&gt;
     // or if the data should be protected/private.&lt;br /&gt;
     double value;&lt;br /&gt;
     string units;&lt;br /&gt;
     // EpicsPropertyCatalog&lt;br /&gt;
     int getNumProperties();&lt;br /&gt;
     EpicsProperty *findProperty(const char *name);    &lt;br /&gt;
     EpicsProperty *getProperty(int index);&lt;br /&gt;
 private:&lt;br /&gt;
     EpicsProperty *property[2];&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 AiRecord::AiRecord()&lt;br /&gt;
 {&lt;br /&gt;
     property[0] = new EpicsDoubleProperty(&amp;quot;value&amp;quot;, &amp;amp;this-&amp;gt;value);&lt;br /&gt;
     property[1] = new EpicsStringProperty(&amp;quot;units&amp;quot;, &amp;amp;this-&amp;gt;units);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 int AiRecord::getNumProperties()&lt;br /&gt;
 {   return sizeof(property)/sizeof(EpicsProperty *); }&lt;br /&gt;
 &lt;br /&gt;
 EpicsProperty *AiRecord::findProperty(const char *name)&lt;br /&gt;
 {   // could support hashed numeric IDs instead of strings&lt;br /&gt;
     int i;&lt;br /&gt;
     for (i=0; i&amp;lt;getNumProperties(); ++i)&lt;br /&gt;
         if (!strcmp(property[i]-&amp;gt;getName(), name))&lt;br /&gt;
             return property[i];&lt;br /&gt;
     return 0;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 EpicsProperty *AiRecord::getProperty(int index)&lt;br /&gt;
 {&lt;br /&gt;
     return (index &amp;gt;=0  &amp;amp;&amp;amp;  index &amp;lt;sizeof(property)/sizeof(EpicsProperty *)) ?&lt;br /&gt;
         property[index] : 0;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
=== Usage Example ===&lt;br /&gt;
 #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 int main()&lt;br /&gt;
 {&lt;br /&gt;
     AiRecord fred;&lt;br /&gt;
     // Record/device support might have direct access to the fields:&lt;br /&gt;
     fred.value = 42.3;&lt;br /&gt;
     fred.units = &amp;quot;a.u.&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     // Other code uses PropertyCatalog&lt;br /&gt;
     EpicsPropertyCatalog *pc = &amp;amp;fred;&lt;br /&gt;
 &lt;br /&gt;
     // It can access known properties in chosen order&lt;br /&gt;
     EpicsProperty *vp = pc-&amp;gt;findProperty(&amp;quot;value&amp;quot;);&lt;br /&gt;
     double d;&lt;br /&gt;
     if (vp &amp;amp;&amp;amp; vp-&amp;gt;getDouble(d))&lt;br /&gt;
         printf(&amp;quot;Value = %.2f\n&amp;quot;, d);&lt;br /&gt;
     else&lt;br /&gt;
         printf(&amp;quot;Cannot get value as double\n&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
     // Or inspect unknown property catalogs&lt;br /&gt;
     int i;&lt;br /&gt;
     for (i=0; i&amp;lt;pc-&amp;gt;getNumProperties(); ++i)&lt;br /&gt;
     {&lt;br /&gt;
         printf(&amp;quot;%-10s = &amp;quot;, pc-&amp;gt;getProperty(i)-&amp;gt;getName());&lt;br /&gt;
         switch (pc-&amp;gt;getProperty(i)-&amp;gt;getType())&lt;br /&gt;
         {&lt;br /&gt;
             case String:&lt;br /&gt;
                 const char *s;&lt;br /&gt;
                 if (pc-&amp;gt;getProperty(i)-&amp;gt;getCstring(s))&lt;br /&gt;
                     printf(&amp;quot;'%s'\n&amp;quot;, s);&lt;br /&gt;
                 else&lt;br /&gt;
                     printf(&amp;quot;&amp;lt;cannot get string&amp;gt;\n&amp;quot;);&lt;br /&gt;
                 break;&lt;br /&gt;
             case Bool:&lt;br /&gt;
             case Integer:&lt;br /&gt;
             case Real:&lt;br /&gt;
                 double d;&lt;br /&gt;
                 if (pc-&amp;gt;getProperty(i)-&amp;gt;getDouble(d))&lt;br /&gt;
                     printf(&amp;quot;%.2f\n&amp;quot;, d);&lt;br /&gt;
                 else&lt;br /&gt;
                     printf(&amp;quot;&amp;lt;cannot get double&amp;gt;\n&amp;quot;);&lt;br /&gt;
                 break;&lt;br /&gt;
             default:&lt;br /&gt;
                 printf(&amp;quot;&amp;lt;unhandled type&amp;gt;\n&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
        return 0;&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=Ancient_V4_Design&amp;diff=455</id>
		<title>Ancient V4 Design</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=Ancient_V4_Design&amp;diff=455"/>
		<updated>2005-07-27T22:16:17Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: data interface&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page links to various documents for discussion by the EPICS core developers.&lt;br /&gt;
&lt;br /&gt;
'''Code to implement this stuff doesn't exist yet, and the designs may change!'''&lt;br /&gt;
&lt;br /&gt;
''Note to authors: please use 'V4' at the start of page names for Version 4 documentation, as this makes it clear which wiki pages refer to the new designs just from the page name.''&lt;br /&gt;
&lt;br /&gt;
I've started listing some discussion topics for a [[V4 July Meeting Agenda]], feel free to add to these - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
== Overall Design ==&lt;br /&gt;
* [[V4 Use Cases]] - Bob Dalesio, Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
== Core and Database ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Modules and Interfaces]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* [[V4 Requirements for Standard Types]] - Andrew Johnson&lt;br /&gt;
* [[V4 Standard Properties and Events]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* V4 Database Definition: [[V4 Database Definition|(old version)]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DBD Statement Syntax]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DBD dbCommon and iocRecord]] - Marty Kraimer&lt;br /&gt;
** [[V4 DBD Examples]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DB Record Instance Syntax]] - Andrew Johnson&lt;br /&gt;
** [[V4 DBD Generated Code]] - Marty Kraimer&lt;br /&gt;
** [[V4 View Generated Code]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* V4 Database Access:&lt;br /&gt;
** [[V4 Design: epicsTypes]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 Design: dbdClass]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: dbdClass Examples]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Database Initialization]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Database Access Semantics]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Record Processing]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Assembling Record Support]] - Ben Franksen&lt;br /&gt;
** [[V4 Design: driverInterfaces]] - Marty Kraimer&lt;br /&gt;
&lt;br /&gt;
* [[V4 User-defined fields]] - Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
* [[V4 Server Side Plugins]] - Ralph Lange, Benjamin Franksen&lt;br /&gt;
&lt;br /&gt;
== Network Communications ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Name Server]] - Doug Murray, Kay Kasemir&lt;br /&gt;
* [[V4 CA Interfaces]] - Jeff Hill&lt;br /&gt;
* [[V4 CA Client User Interface]] - Doug Murray, Kay Kasemir&lt;br /&gt;
* [[V4 Data Interface]] - Kay Kasemir&lt;br /&gt;
* [[V4 CA example timeStamp, sevr, status, data]] - Marty Kraimer&lt;br /&gt;
&lt;br /&gt;
== Other Services ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Replacement for drvTS]] - Timo Korhonen&lt;br /&gt;
* [[V4 Replacement for SNL]] - Ben Franksen, Götz Pfeiffer&lt;br /&gt;
* [[V4 Data Store]] - Ralph Lange&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Use_Cases&amp;diff=557</id>
		<title>V4 Use Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Use_Cases&amp;diff=557"/>
		<updated>2005-07-18T19:30:30Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Tried to format it better. Still ugly.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
These are example use cases where we need to assert that each layer of EPICS V4 handles them:&lt;br /&gt;
&lt;br /&gt;
1. Can the CA client user API articulate the request?&lt;br /&gt;
&lt;br /&gt;
2. Can the network protocol transfer the request?&lt;br /&gt;
&lt;br /&gt;
3. Do the database &amp;amp; drivers understand the request and provide the necessary data?&lt;br /&gt;
&lt;br /&gt;
4. Does the data get back up to the client in the appropriate form and at the requested time?&lt;br /&gt;
&lt;br /&gt;
== Use Cases ==&lt;br /&gt;
&lt;br /&gt;
=== Command-line tools for debugging and scripting ===&lt;br /&gt;
1. Dump the current &amp;quot;value&amp;quot; property of the channel &amp;quot;fred&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
2. Dump all properties, their type, and current value for a given channel.&lt;br /&gt;
&lt;br /&gt;
3. Dump the timestamp, value, units, status, severity of &amp;quot;fred&amp;quot; once a second.&lt;br /&gt;
&lt;br /&gt;
4. ... whenever any of the timestamp, ... severity change.&lt;br /&gt;
&lt;br /&gt;
5. List all events that a PV supports: On change, 1Hz, RF Pulse, Beam Pulse, ...&lt;br /&gt;
&lt;br /&gt;
6. Dump the value of the cavity waveform for every beam pulse.&lt;br /&gt;
&lt;br /&gt;
7. List all channels that match a given name pattern.&lt;br /&gt;
&lt;br /&gt;
=== Display Manager ===&lt;br /&gt;
1. Display an analog input value on a bar:&amp;lt;br&amp;gt;&lt;br /&gt;
Get limits, subscribe to values, receive value &amp;amp; display it, receive change-of-display-range notification,...&lt;br /&gt;
&lt;br /&gt;
2. Display a menu output for a 2 state valve on a text update:&amp;lt;br&amp;gt;&lt;br /&gt;
Get state strings, subscribe to (numeric?) value, display incoming data, receive state string updates, ...&lt;br /&gt;
&lt;br /&gt;
3. Give a controller for a 2 state valve controller on a radio button.&amp;lt;br&amp;gt;Handle same as 2?&lt;br /&gt;
&lt;br /&gt;
4. Display a waveform on an xy plot.&amp;lt;br&amp;gt;&lt;br /&gt;
Similar to 1, but array data?&lt;br /&gt;
&lt;br /&gt;
5. Attempt to display a string on a bar&lt;br /&gt;
&lt;br /&gt;
6. Universal PV Inspector:&amp;lt;br&amp;gt;&lt;br /&gt;
Get properties, allow user to select properties.&amp;lt;br&amp;gt;&lt;br /&gt;
If writeable string -&amp;gt; display text entry field.&amp;lt;br&amp;gt;&lt;br /&gt;
If read-only string -&amp;gt; display text update field.&amp;lt;br&amp;gt;&lt;br /&gt;
If writeable numeric with limits -&amp;gt; display slider with range of number.&amp;lt;br&amp;gt;&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
7. Display as bars, a list of BPMs where I can set the offset in time to fetch (assuming that these BPMs are read as arrays that are all triggered at one time – but I want to see them at different points in time as it goes down the beamline).&lt;br /&gt;
&lt;br /&gt;
=== Physics Application ===&lt;br /&gt;
&lt;br /&gt;
BPM-related ideas&lt;br /&gt;
&lt;br /&gt;
1. List all channels available that are BPMs&lt;br /&gt;
&lt;br /&gt;
2.  Select all of them in sector 6&lt;br /&gt;
&lt;br /&gt;
3. Display them when the beam is going to the end station A (one of 8 destinations possible)&lt;br /&gt;
&lt;br /&gt;
4. Same, but display all BPMs on the .1 Hz event&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
Scan a probe in the beam line&lt;br /&gt;
&lt;br /&gt;
1. Find PVs for some beam line device:&amp;lt;br&amp;gt;&lt;br /&gt;
Stepper motors for probe's X &amp;amp; Y position, intensity of beam transmitted by probe,&lt;br /&gt;
beam shutter open/close, beam available&lt;br /&gt;
&lt;br /&gt;
2. Scan sequence:&amp;lt;br&amp;gt;&lt;br /&gt;
 Assert shutter is closed;&lt;br /&gt;
 move X/Y to 0/0, wait for completion&lt;br /&gt;
 until done:&lt;br /&gt;
    open shutter, wait until open&lt;br /&gt;
    measure intensity for 1 second while beam_available&lt;br /&gt;
    close shutter&lt;br /&gt;
    move to next position, wait until you get there&lt;br /&gt;
&lt;br /&gt;
=== Archiver ===&lt;br /&gt;
1. Get value, timestamp, status/severity of a channel once per second&lt;br /&gt;
&lt;br /&gt;
2. Archive default properties of a channel using the default deadband.&amp;lt;br&amp;gt;&lt;br /&gt;
How are they defined? How does the archiver learn about them?&lt;br /&gt;
&lt;br /&gt;
3. Given that there is a new view defined in the stepper motor that includes all motor readback data:Subscribe to counterClockWiseLimit, clockWiseLimit, moving, doneRetries, homeLimit at 10 Hz.&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Use_Cases&amp;diff=439</id>
		<title>V4 Use Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Use_Cases&amp;diff=439"/>
		<updated>2005-07-18T19:15:20Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Use Cases ==&lt;br /&gt;
&lt;br /&gt;
These are examples that need to get handled&lt;br /&gt;
by the CA client user API, the network protocol,&lt;br /&gt;
the database, down to drivers and back up to&lt;br /&gt;
the client.&lt;br /&gt;
&lt;br /&gt;
=== Command-line tools for debugging and scripting ===&lt;br /&gt;
1. Dump the current &amp;quot;value&amp;quot; of the channel &amp;quot;fred&amp;quot;.&lt;br /&gt;
2. Dump all properties, their type, current value for a given channel.&lt;br /&gt;
3. Dump the timestamp, value, units, status, severity of &amp;quot;fred&amp;quot; once a second.&lt;br /&gt;
4. ... whenever any of the timestamp, ... severity change.&lt;br /&gt;
5. List all events that a PV supports: On change, 1Hz, RF Pulse, Beam Pulse, ...&lt;br /&gt;
6. Dump the value of the cavity waveform for every beam pulse.&lt;br /&gt;
&lt;br /&gt;
=== Display Manager ===&lt;br /&gt;
1. Display an analog input value on a bar&lt;br /&gt;
 Get limits, subscribe to values, receive value &amp;amp; display it, receive change-of-display-range notification,...&lt;br /&gt;
2. Display a menu output for a 2 state valve on a text update&lt;br /&gt;
 Get state strings, subscribe to (numeric?) value, display incoming data, receive state string updates, ...&lt;br /&gt;
3. Give a controller for a 2 state valve controller on a radio button&lt;br /&gt;
 Handle same as 2?&lt;br /&gt;
4. Display a waveform on an xy plot&lt;br /&gt;
 Similar to 1, but array data?&lt;br /&gt;
5. Attempt to display a string on a bar&lt;br /&gt;
6. Universal PV Inspector:&lt;br /&gt;
 Get properties, allow user to select properties.&lt;br /&gt;
 If writeable string -&amp;gt; display text entry field&lt;br /&gt;
 If read-only string -&amp;gt; display text update field&lt;br /&gt;
 If writeable numeric with limits -&amp;gt; display slider with range of number&lt;br /&gt;
 ....&lt;br /&gt;
7. Display as bars, a list of BPMs where I can set the offset in time to fetch (assuming that these BPMs are read as arrays that are all triggered at one time – but I want to see them at different points in time as it goes down the beamline).&lt;br /&gt;
&lt;br /&gt;
=== Physics Application ===&lt;br /&gt;
1. BPM-related ideas&lt;br /&gt;
1.1. List all channels available that are BPMs&lt;br /&gt;
1.2. Select all of them in sector 6&lt;br /&gt;
1.3 Display them when the beam is going to the end station A (one of 8 destinations possible)&lt;br /&gt;
1.4 Same, but display all BPMs on the .1 Hz event&lt;br /&gt;
 &lt;br /&gt;
2. Scan a probe in the beam line&lt;br /&gt;
2.1 Find PVs for some beam line device:&lt;br /&gt;
    Stepper motors for probe's X &amp;amp; Y position, intensity of beam transmitted by probe,&lt;br /&gt;
    beam shutter open/close, beam available&lt;br /&gt;
2.2 Scan sequence:&lt;br /&gt;
    Assert shutter is closed;&lt;br /&gt;
    move X/Y to 0/0, wait for completion&lt;br /&gt;
    until done:&lt;br /&gt;
       open shutter, wait until open&lt;br /&gt;
       measure intensity for 1 second while beam_available&lt;br /&gt;
       close shutter&lt;br /&gt;
       move to next position, wait until you get there&lt;br /&gt;
&lt;br /&gt;
=== Archiver ===&lt;br /&gt;
1. Get value, timestamp, status/severity of a channel once per second&lt;br /&gt;
2. Archive default properties of a channel using the default deadband&lt;br /&gt;
  How are they defined? How does the archiver learn about them?&lt;br /&gt;
3. Given that there is a new view defined in the stepper motor that includes all motor readback data:&lt;br /&gt;
 Subscribe to counterClockWiseLimit, clockWiseLimit, moving, doneRetries, homeLimit&lt;br /&gt;
 at 10 Hz.&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=Ancient_V4_Design&amp;diff=448</id>
		<title>Ancient V4 Design</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=Ancient_V4_Design&amp;diff=448"/>
		<updated>2005-07-18T19:11:56Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page links to various documents for discussion by the EPICS core developers.&lt;br /&gt;
&lt;br /&gt;
'''Code to implement this stuff doesn't exist yet, and the designs may change!'''&lt;br /&gt;
&lt;br /&gt;
''Note to authors: please use 'V4' at the start of page names for Version 4 documentation, as this makes it clear which wiki pages refer to the new designs just from the page name.''&lt;br /&gt;
&lt;br /&gt;
I've started listing some discussion topics for a [[V4 July Meeting Agenda]], feel free to add to these - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
== Overall Design ==&lt;br /&gt;
* [[V4 Use Cases]] - Bob Dalesio, Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
== Core and Database ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Modules and Interfaces]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* [[V4 Requirements for Standard Types]] - Andrew Johnson&lt;br /&gt;
* [[V4 Standard Properties and Events]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* V4 Database Definition: [[V4 Database Definition|(old version)]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DBD Statement Syntax]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DBD dbCommon and iocRecord]] - Marty Kraimer&lt;br /&gt;
** [[V4 DBD Examples]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DB Record Instance Syntax]] - Andrew Johnson&lt;br /&gt;
** [[V4 DBD Generated Code]] - Marty Kraimer&lt;br /&gt;
** [[V4 View Generated Code]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* V4 Database Access:&lt;br /&gt;
** [[V4 Design: epicsTypes]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 Design: dbdClass]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: dbdClass Examples]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Database Initialization]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Database Access Semantics]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Record Processing]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Assembling Record Support]] - Ben Franksen&lt;br /&gt;
** [[V4 Design: driverInterfaces]] - Marty Kraimer&lt;br /&gt;
&lt;br /&gt;
* [[V4 User-defined fields]] - Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
* [[V4 Server Side Plugins]] - Ralph Lange, Benjamin Franksen&lt;br /&gt;
&lt;br /&gt;
== Network Communications ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Name Server]] - Doug Murray, Kay Kasemir&lt;br /&gt;
* [[V4 CA Interfaces]] - Jeff Hill&lt;br /&gt;
* [[V4 CA Client User Interface]] - Doug Murray, Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
== Other Services ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Replacement for drvTS]] - Timo Korhonen&lt;br /&gt;
* [[V4 Replacement for SNL]] - Ben Franksen, Götz Pfeiffer&lt;br /&gt;
* [[V4 Data Store]] - Ralph Lange&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=Ancient_V4_Design&amp;diff=438</id>
		<title>Ancient V4 Design</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=Ancient_V4_Design&amp;diff=438"/>
		<updated>2005-07-18T19:11:32Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: use cases&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page links to various documents for discussion by the EPICS core developers.&lt;br /&gt;
&lt;br /&gt;
'''Code to implement this stuff doesn't exist yet, and the designs may change!'''&lt;br /&gt;
&lt;br /&gt;
''Note to authors: please use 'V4' at the start of page names for Version 4 documentation, as this makes it clear which wiki pages refer to the new designs just from the page name.''&lt;br /&gt;
&lt;br /&gt;
I've started listing some discussion topics for a [[V4 July Meeting Agenda]], feel free to add to these - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
== Overall Design ==&lt;br /&gt;
* [[V4 Use Cases]] - Bob Dalesio, Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
== Core and Database ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Modules and Interfaces]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* [[V4 Requirements for Standard Types]] - Andrew Johnson&lt;br /&gt;
* [[V4 Standard Properties and Events]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* V4 Database Definition: [[V4 Database Definition|(old version)]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DBD Statement Syntax]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DBD dbCommon and iocRecord]] - Marty Kraimer&lt;br /&gt;
** [[V4 DBD Examples]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DB Record Instance Syntax]] - Andrew Johnson&lt;br /&gt;
** [[V4 DBD Generated Code]] - Marty Kraimer&lt;br /&gt;
** [[V4 View Generated Code]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* V4 Database Access:&lt;br /&gt;
** [[V4 Design: epicsTypes]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 Design: dbdClass]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: dbdClass Examples]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Database Initialization]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Database Access Semantics]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Record Processing]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Assembling Record Support]] - Ben Franksen&lt;br /&gt;
** [[V4 Design: driverInterfaces]] - Marty Kraimer&lt;br /&gt;
&lt;br /&gt;
* [[V4 User-defined fields]] - Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
* [[V4 Server Side Plugins]] - Ralph Lange, Benjamin Franksen&lt;br /&gt;
&lt;br /&gt;
== Core and Database ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Modules and Interfaces]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* [[V4 Requirements for Standard Types]] - Andrew Johnson&lt;br /&gt;
* [[V4 Standard Properties and Events]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* V4 Database Definition: [[V4 Database Definition|(old version)]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DBD Statement Syntax]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DBD dbCommon and iocRecord]] - Marty Kraimer&lt;br /&gt;
** [[V4 DBD Examples]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DB Record Instance Syntax]] - Andrew Johnson&lt;br /&gt;
** [[V4 DBD Generated Code]] - Marty Kraimer&lt;br /&gt;
** [[V4 View Generated Code]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* V4 Database Access:&lt;br /&gt;
** [[V4 Design: epicsTypes]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 Design: dbdClass]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: dbdClass Examples]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Database Initialization]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Database Access Semantics]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Record Processing]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Assembling Record Support]] - Ben Franksen&lt;br /&gt;
** [[V4 Design: driverInterfaces]] - Marty Kraimer&lt;br /&gt;
&lt;br /&gt;
* [[V4 User-defined fields]] - Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
* [[V4 Server Side Plugins]] - Ralph Lange, Benjamin Franksen&lt;br /&gt;
&lt;br /&gt;
== Network Communications ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Name Server]] - Doug Murray, Kay Kasemir&lt;br /&gt;
* [[V4 CA Interfaces]] - Jeff Hill&lt;br /&gt;
* [[V4 CA Client User Interface]] - Doug Murray, Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
== Other Services ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Replacement for drvTS]] - Timo Korhonen&lt;br /&gt;
* [[V4 Replacement for SNL]] - Ben Franksen, Götz Pfeiffer&lt;br /&gt;
* [[V4 Data Store]] - Ralph Lange&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=Ancient_V4_Design&amp;diff=437</id>
		<title>Ancient V4 Design</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=Ancient_V4_Design&amp;diff=437"/>
		<updated>2005-07-18T19:11:28Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: use cases&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page links to various documents for discussion by the EPICS core developers.&lt;br /&gt;
&lt;br /&gt;
'''Code to implement this stuff doesn't exist yet, and the designs may change!'''&lt;br /&gt;
&lt;br /&gt;
''Note to authors: please use 'V4' at the start of page names for Version 4 documentation, as this makes it clear which wiki pages refer to the new designs just from the page name.''&lt;br /&gt;
&lt;br /&gt;
I've started listing some discussion topics for a [[V4 July Meeting Agenda]], feel free to add to these - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
== Overall Design ==&lt;br /&gt;
* [[V4 Use Cases]] - Bob Dalesio, Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
== Core and Database ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Modules and Interfaces]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* [[V4 Requirements for Standard Types]] - Andrew Johnson&lt;br /&gt;
* [[V4 Standard Properties and Events]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* V4 Database Definition: [[V4 Database Definition|(old version)]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DBD Statement Syntax]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DBD dbCommon and iocRecord]] - Marty Kraimer&lt;br /&gt;
** [[V4 DBD Examples]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 DB Record Instance Syntax]] - Andrew Johnson&lt;br /&gt;
** [[V4 DBD Generated Code]] - Marty Kraimer&lt;br /&gt;
** [[V4 View Generated Code]] - Andrew Johnson&lt;br /&gt;
&lt;br /&gt;
* V4 Database Access:&lt;br /&gt;
** [[V4 Design: epicsTypes]] - Marty Kraimer, Andrew Johnson&lt;br /&gt;
** [[V4 Design: dbdClass]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: dbdClass Examples]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Database Initialization]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Database Access Semantics]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Record Processing]] - Marty Kraimer&lt;br /&gt;
** [[V4 Design: Assembling Record Support]] - Ben Franksen&lt;br /&gt;
** [[V4 Design: driverInterfaces]] - Marty Kraimer&lt;br /&gt;
&lt;br /&gt;
* [[V4 User-defined fields]] - Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
* [[V4 Server Side Plugins]] - Ralph Lange, Benjamin Franksen&lt;br /&gt;
&lt;br /&gt;
== Network Communications ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Name Server]] - Doug Murray, Kay Kasemir&lt;br /&gt;
* [[V4 CA Interfaces]] - Jeff Hill&lt;br /&gt;
* [[V4 CA Client User Interface]] - Doug Murray, Kay Kasemir&lt;br /&gt;
&lt;br /&gt;
== Other Services ==&lt;br /&gt;
&lt;br /&gt;
* [[V4 Replacement for drvTS]] - Timo Korhonen&lt;br /&gt;
* [[V4 Replacement for SNL]] - Ben Franksen, Götz Pfeiffer&lt;br /&gt;
* [[V4 Data Store]] - Ralph Lange&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=2712</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=2712"/>
		<updated>2005-07-16T19:13:44Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: TIPC&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
* TIPC, the Transparent Inter Process Communication protocol (TIPC) started out as a communications layer for building clusters, I think by Ericson, and is now becoming more open and also supported by  the latest WindRiver vxWorks releases.&amp;lt;br&amp;gt;The Buzz:&lt;br /&gt;
** allows socket-based applications to communicate easily and efficient&lt;br /&gt;
** based on publicly available source code&lt;br /&gt;
** A location-transparent addressing scheme&lt;br /&gt;
** Efficient, rapid, and reliable interprocess communication within a node and between nodes, using either connection-oriented or connectionless modes of operation&lt;br /&gt;
** Rapid notification of changes in topology and the ability to adjust quickly to these changes&lt;br /&gt;
&lt;br /&gt;
== Constraints ==&lt;br /&gt;
* Initial implementation will be in C++&lt;br /&gt;
* Should very easily translate into Java, just like e.g. some XML APIs use the exact same classes and methods in C++ and Java. Ideally, Java and C++ version will be available from the start.&lt;br /&gt;
* Ideally, the C++ headers would be simple enough to be parsed by SWIG and hence perl/tcl/python/... glue code could be auto-generated via SWIG&lt;br /&gt;
* API is of course specific to EPICS CA, but ideally it'll also be usable as an interface to other protocols, so that EPICS Office can use an API that is only a slight extension of the CA client user API.&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data ===&lt;br /&gt;
The available ''data'' constists of property names, types and values, for example:&lt;br /&gt;
     double     value; // The &amp;quot;value&amp;quot; property of our data&lt;br /&gt;
     string     units; // The &amp;quot;units&amp;quot; property....&lt;br /&gt;
     time_stamp time;&lt;br /&gt;
  &lt;br /&gt;
The data might contain arrays and structures.&lt;br /&gt;
The user might not want all the properties, and the user might also want to get properties in non-native types (strings for convenience, float instead of double to preserve space, ...).&lt;br /&gt;
The client has to provide storage, and might allocate this:&lt;br /&gt;
     float  value;&lt;br /&gt;
     string units;&lt;br /&gt;
&lt;br /&gt;
The user interface must provide the following:&lt;br /&gt;
* getAllProperties()&amp;lt;BR&amp;gt;Get a list of all the property names and their data type so that the client can make an informed decision for allocating its storage of the data. There might be helper routines to convert between human-usable strings and more efficient numeric identifiers.&lt;br /&gt;
* bool hasWriteAccess(string property)&amp;lt;BR&amp;gt;Could also be part of the info return by previous method.&lt;br /&gt;
&lt;br /&gt;
This functionality is available without getting any actual data.&lt;br /&gt;
To retrieve values, the user asks the client library to place the data into&lt;br /&gt;
the client-provided storage.&lt;br /&gt;
The ''traverse'' of the &amp;quot;dataAccess&amp;quot; proposal handles this by asking&lt;br /&gt;
the user to implement a property catalog that ''reveals'' each storage element.&lt;br /&gt;
The end effect is not too different from having the user invoke methods&lt;br /&gt;
like these:&lt;br /&gt;
* getAsString(string property, string *my_storage)&lt;br /&gt;
* getAsDouble(string property, double *my_storage)&lt;br /&gt;
* ...many more, one for each available data type.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime. To avoid security issues, setUser will probably send a public key and not a user name and password.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion, response})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value (delivery) or  all the processing triggered by the new value has completed (completion). In addition, some records might support command/response, in which case one can write a command and wait for a response callback which looks just like the result of a read request.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercentageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== ChannelGroup ===&lt;br /&gt;
A channel group synchronizes access to multiple channels.&lt;br /&gt;
It listens to all members of the channel group and for example&lt;br /&gt;
allows waiting for ''all'' outstanding read or write requests&lt;br /&gt;
to return.&lt;br /&gt;
&lt;br /&gt;
* add/remove channels&amp;lt;br&amp;gt;By name? Already existing Channel? Add channels that match a pattern, or go via the directory?&lt;br /&gt;
* read/write&amp;lt;br&amp;gt;Has to handle reading/writing the same properties from all channels in group. Also channel-specific list of properties?&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
Also see [[V4 Data Store]]&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
We can imagine an even simpler example:&lt;br /&gt;
&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of fred:&amp;quot; &amp;lt;&amp;lt; Epics::get(&amp;quot;fred&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
This implies lots of work under the hood, with reasonable defaults.&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=435</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=435"/>
		<updated>2005-07-16T05:10:53Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: start over with requirements, dropping details that are controversial&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Constraints ==&lt;br /&gt;
* Initial implementation will be in C++&lt;br /&gt;
* Should very easily translate into Java, just like e.g. some XML APIs use the exact same classes and methods in C++ and Java. Ideally, Java and C++ version will be available from the start.&lt;br /&gt;
* Ideally, the C++ headers would be simple enough to be parsed by SWIG and hence perl/tcl/python/... glue code could be auto-generated via SWIG&lt;br /&gt;
* API is of course specific to EPICS CA, but ideally it'll also be usable as an interface to other protocols, so that EPICS Office can use an API that is only a slight extension of the CA client user API.&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data ===&lt;br /&gt;
The available ''data'' constists of property names, types and values, for example:&lt;br /&gt;
     double     value; // The &amp;quot;value&amp;quot; property of our data&lt;br /&gt;
     string     units; // The &amp;quot;units&amp;quot; property....&lt;br /&gt;
     time_stamp time;&lt;br /&gt;
  &lt;br /&gt;
The data might contain arrays and structures.&lt;br /&gt;
The user might not want all the properties, and the user might also want to get properties in non-native types (strings for convenience, float instead of double to preserve space, ...).&lt;br /&gt;
The client has to provide storage, and might allocate this:&lt;br /&gt;
     float  value;&lt;br /&gt;
     string units;&lt;br /&gt;
&lt;br /&gt;
The user interface must provide the following:&lt;br /&gt;
* getAllProperties()&amp;lt;BR&amp;gt;Get a list of all the property names and their data type so that the client can make an informed decision for allocating its storage of the data. There might be helper routines to convert between human-usable strings and more efficient numeric identifiers.&lt;br /&gt;
* bool hasWriteAccess(string property)&amp;lt;BR&amp;gt;Could also be part of the info return by previous method.&lt;br /&gt;
&lt;br /&gt;
This functionality is available without getting any actual data.&lt;br /&gt;
To retrieve values, the user asks the client library to place the data into&lt;br /&gt;
the client-provided storage.&lt;br /&gt;
The ''traverse'' of the &amp;quot;dataAccess&amp;quot; proposal handles this by asking&lt;br /&gt;
the user to implement a property catalog that ''reveals'' each storage element.&lt;br /&gt;
The end effect is not too different from having the user invoke methods&lt;br /&gt;
like these:&lt;br /&gt;
* getAsString(string property, string *my_storage)&lt;br /&gt;
* getAsDouble(string property, double *my_storage)&lt;br /&gt;
* ...many more, one for each available data type.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime. To avoid security issues, setUser will probably send a public key and not a user name and password.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion, response})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value (delivery) or  all the processing triggered by the new value has completed (completion). In addition, some records might support command/response, in which case one can write a command and wait for a response callback which looks just like the result of a read request.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercentageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== ChannelGroup ===&lt;br /&gt;
A channel group synchronizes access to multiple channels.&lt;br /&gt;
It listens to all members of the channel group and for example&lt;br /&gt;
allows waiting for ''all'' outstanding read or write requests&lt;br /&gt;
to return.&lt;br /&gt;
&lt;br /&gt;
* add/remove channels&amp;lt;br&amp;gt;By name? Already existing Channel? Add channels that match a pattern, or go via the directory?&lt;br /&gt;
* read/write&amp;lt;br&amp;gt;Has to handle reading/writing the same properties from all channels in group. Also channel-specific list of properties?&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
Also see [[V4 Data Store]]&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
We can imagine an even simpler example:&lt;br /&gt;
&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of fred:&amp;quot; &amp;lt;&amp;lt; Epics::get(&amp;quot;fred&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
This implies lots of work under the hood, with reasonable defaults.&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_User-defined_fields&amp;diff=2710</id>
		<title>V4 User-defined fields</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_User-defined_fields&amp;diff=2710"/>
		<updated>2005-07-14T22:28:25Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: /* Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Records, the building blocks of an EPICS run-time database,&lt;br /&gt;
are much more powerful than the fundamental commands&lt;br /&gt;
of programming languages like C, C++, LabVIEW or PLC&lt;br /&gt;
ladder logic: A record like the AI record provides scanning,&lt;br /&gt;
various conversions, alarms, smoothing and more.&lt;br /&gt;
&lt;br /&gt;
On the downside, very few record instances use the full functionality&lt;br /&gt;
of the record type. Extending their functionality is rather involved,&lt;br /&gt;
and very few custom record types get shared in the community.&lt;br /&gt;
&lt;br /&gt;
If it was easier to create, modify and extend records,&lt;br /&gt;
we could start with simpler record types and extend them&lt;br /&gt;
as needed.&lt;br /&gt;
&lt;br /&gt;
This is a suggestion for supporting user-defined fields in V4,&lt;br /&gt;
allowing the user to add fields to a record instance without&lt;br /&gt;
first having to create a new record type.&lt;br /&gt;
&lt;br /&gt;
By now, the 'Vampire' (see [[V4_Server_Side_Plugins]]) seems to handle all but the forward link,&lt;br /&gt;
and seems an overall better idea.&lt;br /&gt;
&lt;br /&gt;
== User-defined fields ==&lt;br /&gt;
&lt;br /&gt;
For example, I'd like to allow users to define &amp;quot;analog input&amp;quot; records&lt;br /&gt;
with the usual fields '''and''' allow them to add new fields to some&lt;br /&gt;
instance without having to create a new record type:&lt;br /&gt;
&lt;br /&gt;
 calc(&amp;quot;fred&amp;quot;)&lt;br /&gt;
 {&lt;br /&gt;
    field(SCAN, &amp;quot;1 second&amp;quot;)&lt;br /&gt;
    user_field(SMOO,  iocFloat64T, &amp;quot;0.5&amp;quot;, smoo_handler)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This record will have a 'SMOO' field of type double (float 64) that presumably implements&lt;br /&gt;
smoothing in the user-supplied smoo_handler.&lt;br /&gt;
&lt;br /&gt;
The idea is that one can add these user-defined fields on a per-instance basis&lt;br /&gt;
to pretty much any record type simply by editing the DB file and loading the&lt;br /&gt;
handler code, without having to recompile the rest of EPICS base.&lt;br /&gt;
&lt;br /&gt;
Example code for a ''handler'' and what the ''database'' code would have to do&lt;br /&gt;
follows.&lt;br /&gt;
&lt;br /&gt;
=== database.h ===&lt;br /&gt;
&lt;br /&gt;
 #include &amp;lt;iostream&amp;gt;&lt;br /&gt;
 #include &amp;lt;string&amp;gt;&lt;br /&gt;
 #include &amp;lt;list&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 using namespace std;&lt;br /&gt;
 &lt;br /&gt;
 enum iocType&lt;br /&gt;
 {&lt;br /&gt;
     iocUnknownT,iocBooleanT,iocOctetT,&lt;br /&gt;
     iocInt16T,iocUInt16T,iocInt32T,iocUInt32T,iocInt64T,iocUInt64T,&lt;br /&gt;
     iocFloat32T,iocFloat64T,&lt;br /&gt;
     iocStringT,iocMenuT,iocEnumT,iocLinkT,iocDeviceT,iocArrayT&lt;br /&gt;
 };&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // Support for user-defined fields in base&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // forwards&lt;br /&gt;
 class record;&lt;br /&gt;
 class user_field_handler;&lt;br /&gt;
 &lt;br /&gt;
 // Information for one user-defined field&lt;br /&gt;
 class user_field_data&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     string  name;&lt;br /&gt;
     iocType type;&lt;br /&gt;
     void    *data;&lt;br /&gt;
     string  parm;&lt;br /&gt;
     user_field_handler *handler;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // The 'handler' specified for a user field needs to implement&lt;br /&gt;
 // this interface:&lt;br /&gt;
 class user_field_handler&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual void init(user_field_data *field, record *rec)     {}&lt;br /&gt;
     virtual void update(user_field_data *field, record *rec)   {}&lt;br /&gt;
     virtual void destroy(user_field_data *field, record *rec)  {}  &lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // Records need to support hooks similar to Java 'listeners'&lt;br /&gt;
 // so that user fields can hook into record processing.&lt;br /&gt;
 // Unclear at what levels hooks are needed.&lt;br /&gt;
 class got_data_hook&lt;br /&gt;
 {&lt;br /&gt;
 public:    virtual void run_got_data_hook()=0;&lt;br /&gt;
 };&lt;br /&gt;
 class post_monitor_hook&lt;br /&gt;
 {&lt;br /&gt;
 public:    virtual void run_post_monitor_hook()=0;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // Pieces of record code in order to show where the hooks&lt;br /&gt;
 // are handled inside of 'process()'&lt;br /&gt;
 class record&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     record(string name) : name(name) {}&lt;br /&gt;
     &lt;br /&gt;
     void add_user_field(string name, iocType type, string parm,&lt;br /&gt;
                         user_field_handler *handler)&lt;br /&gt;
     {&lt;br /&gt;
         user_field_data ufd;&lt;br /&gt;
         ufd.name = name;&lt;br /&gt;
         ufd.type = type;&lt;br /&gt;
         ufd.parm = parm;&lt;br /&gt;
         ufd.handler = handler;&lt;br /&gt;
         user_fields.push_back(ufd);&lt;br /&gt;
     }&lt;br /&gt;
     void add_got_data_hook(got_data_hook *hook)&lt;br /&gt;
     {&lt;br /&gt;
         got_data_hooks.push_back(hook);&lt;br /&gt;
     }&lt;br /&gt;
     // TODO: remove_got_data_hook();&lt;br /&gt;
     void add_post_monitor_hook(post_monitor_hook *hook)&lt;br /&gt;
     {&lt;br /&gt;
         post_monitor_hooks.push_back(hook);&lt;br /&gt;
     }&lt;br /&gt;
     // TODO: remove_post_monitor_hook();&lt;br /&gt;
 &lt;br /&gt;
     void iocInit();&lt;br /&gt;
 &lt;br /&gt;
     void process();&lt;br /&gt;
 private:&lt;br /&gt;
     string                     name;&lt;br /&gt;
     list&amp;lt;user_field_data&amp;gt;      user_fields;&lt;br /&gt;
     list&amp;lt;got_data_hook *&amp;gt;      got_data_hooks;&lt;br /&gt;
     list&amp;lt;post_monitor_hook *&amp;gt;  post_monitor_hooks;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== database.cpp ===&lt;br /&gt;
&lt;br /&gt;
 #include &amp;quot;database.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 void *get_iocType_mem(iocType type)&lt;br /&gt;
 {&lt;br /&gt;
     switch (type)&lt;br /&gt;
     {&lt;br /&gt;
         case iocFloat32T: return new float;&lt;br /&gt;
         case iocFloat64T: return new double;&lt;br /&gt;
         case iocStringT:  return new string;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 void record::iocInit()&lt;br /&gt;
 {&lt;br /&gt;
     cout &amp;lt;&amp;lt; &amp;quot;record::iocInit(&amp;quot; &amp;lt;&amp;lt; name &amp;lt;&amp;lt; &amp;quot;)\n&amp;quot;;&lt;br /&gt;
     list&amp;lt;user_field_data&amp;gt;::iterator ufi;&lt;br /&gt;
     for (ufi = user_fields.begin();  ufi != user_fields.end();  ++ufi)&lt;br /&gt;
     {&lt;br /&gt;
         if ((ufi-&amp;gt;data = get_iocType_mem(ufi-&amp;gt;type)) != 0)&lt;br /&gt;
             ufi-&amp;gt;handler-&amp;gt;init(&amp;amp;(*ufi), this);&lt;br /&gt;
         else&lt;br /&gt;
             ufi-&amp;gt;type = iocUnknownT;&lt;br /&gt;
     }   &lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 void record::process()&lt;br /&gt;
 {&lt;br /&gt;
     cout &amp;lt;&amp;lt; &amp;quot;record::process(&amp;quot; &amp;lt;&amp;lt; name &amp;lt;&amp;lt; &amp;quot;)\n&amp;quot;;&lt;br /&gt;
 #ifdef TODO&lt;br /&gt;
     status=readValue(pai); /* read the new value */&lt;br /&gt;
     if ( !pact &amp;amp;&amp;amp; pai-&amp;gt;pact ) return(0);&lt;br /&gt;
     pai-&amp;gt;pact = TRUE;&lt;br /&gt;
     &lt;br /&gt;
     recGblGetTimeStamp(pai);&lt;br /&gt;
     if (status==0) convert(pai);&lt;br /&gt;
     else if (status==2) status=0;&lt;br /&gt;
 #endif&lt;br /&gt;
     list&amp;lt;got_data_hook *&amp;gt;::iterator gdhi;&lt;br /&gt;
     for (gdhi = got_data_hooks.begin(); gdhi != got_data_hooks.end(); ++gdhi)&lt;br /&gt;
         (*gdhi)-&amp;gt;run_got_data_hook();&lt;br /&gt;
 #ifdef TODO&lt;br /&gt;
     checkAlarms(pai);&lt;br /&gt;
     monitor(pai);&lt;br /&gt;
 #endif&lt;br /&gt;
     list&amp;lt;post_monitor_hook *&amp;gt;::iterator pmhi;&lt;br /&gt;
     for (pmhi=post_monitor_hooks.begin(); pmhi!=post_monitor_hooks.end(); ++pmhi)&lt;br /&gt;
         (*pmhi)-&amp;gt;run_post_monitor_hook();&lt;br /&gt;
 #ifdef TODO&lt;br /&gt;
     pai-&amp;gt;pact=FALSE;&lt;br /&gt;
 #endif&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // TODO:&lt;br /&gt;
 // Record will need to check all it's user_fields[].name&lt;br /&gt;
 // in case CA tries to get/put.&lt;br /&gt;
&lt;br /&gt;
=== example.cpp ===&lt;br /&gt;
&lt;br /&gt;
 /* Goals:&lt;br /&gt;
 - Avg or other statistical calc:&lt;br /&gt;
   deposits calculated data into new fields,&lt;br /&gt;
   doesn't affect the rest of the record.&lt;br /&gt;
 - Circ Buffer (archive record):&lt;br /&gt;
   Similar, does something with the current VAL&lt;br /&gt;
   outside of the record.&lt;br /&gt;
 - Put Logging: Simlar.&lt;br /&gt;
 - SMOO: Can it modify the VAL field?&lt;br /&gt;
 - BPM combiner of 4 AIs: Unclear.&lt;br /&gt;
 - conversions (new breakpt. table): unclear.&lt;br /&gt;
 - FLNK (w/ delays?): Should happen _after_ all else is done.&lt;br /&gt;
 &lt;br /&gt;
 The following implements handlers for some of the above.&lt;br /&gt;
 &lt;br /&gt;
 */&lt;br /&gt;
 #include &amp;quot;database.h&amp;quot;&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // MAX (easier to calc. then e.g. running average&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 class max_handler : public user_field_handler, got_data_hook&lt;br /&gt;
 {&lt;br /&gt;
     void init(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         max = (double *)field-&amp;gt;data;&lt;br /&gt;
         // TODO: val = record-&amp;gt;get_field_addr(&amp;quot;VAL&amp;quot;)&lt;br /&gt;
         rec-&amp;gt;add_got_data_hook(this);&lt;br /&gt;
     }&lt;br /&gt;
     void run_got_data_hook()&lt;br /&gt;
     {&lt;br /&gt;
         // if (*val &amp;gt; *max)   *max = *val;&lt;br /&gt;
         cout &amp;lt;&amp;lt; &amp;quot;Max is &amp;quot; &amp;lt;&amp;lt; *max &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     double *max;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // SMOO&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 class smoo_handler : public user_field_handler, got_data_hook&lt;br /&gt;
 {&lt;br /&gt;
     void init(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         smoo = (double *)field-&amp;gt;data;&lt;br /&gt;
         *smoo = atof(field-&amp;gt;parm.c_str());&lt;br /&gt;
         // TODO: val = record-&amp;gt;get_field_addr(&amp;quot;VAL&amp;quot;)&lt;br /&gt;
         rec-&amp;gt;add_got_data_hook(this);&lt;br /&gt;
     }&lt;br /&gt;
     void run_got_data_hook()&lt;br /&gt;
     {&lt;br /&gt;
         // *val = smoo*(*val) + (1-smoo)*prev_value;&lt;br /&gt;
         // prev_value = *val;&lt;br /&gt;
         cout &amp;lt;&amp;lt; &amp;quot;Smoothing by &amp;quot; &amp;lt;&amp;lt; *smoo &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     double *smoo;&lt;br /&gt;
     double prev_value;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // FLNK&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 class flnk_handler : public user_field_handler, post_monitor_hook&lt;br /&gt;
 {&lt;br /&gt;
     void init(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         link_text = (string *)field-&amp;gt;data;&lt;br /&gt;
         *link_text = field-&amp;gt;parm;&lt;br /&gt;
         update(field, rec);&lt;br /&gt;
         rec-&amp;gt;add_post_monitor_hook(this);&lt;br /&gt;
     }&lt;br /&gt;
     void update(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         // TODO: ink_info = parse_link_info_from_string(*link_text);&lt;br /&gt;
         link_info = *link_text;&lt;br /&gt;
     }&lt;br /&gt;
     void run_post_monitor_hook()&lt;br /&gt;
     {&lt;br /&gt;
         cout &amp;lt;&amp;lt; &amp;quot;FLNKing to &amp;quot; &amp;lt;&amp;lt; link_info &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
         // TODO: dbProcessLink(this-&amp;gt;link_info);&lt;br /&gt;
         // Or with additional delay parm:&lt;br /&gt;
         // wdStart(delay, dbProcessLink, this-&amp;gt;link_info);  ?&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     string *link_text;&lt;br /&gt;
     string link_info;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // Example database snippet and how it's used&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 /*&lt;br /&gt;
 ai(&amp;quot;fred&amp;quot;)&lt;br /&gt;
 {&lt;br /&gt;
   #          NAME, TYPE,         PARM,      HANDLER&lt;br /&gt;
   user_field(smoo,  iocFloat64T, &amp;quot;0.5&amp;quot;,     smoo_handler)&lt;br /&gt;
   user_field(max,   iocFloat64T, &amp;quot;&amp;quot;,        max_handler)&lt;br /&gt;
   user_field(flnk1, iocStringT,  &amp;quot;recordA&amp;quot;, flnk_handler)&lt;br /&gt;
   user_field(flnk2, iocStringT,  &amp;quot;recordB&amp;quot;, flnk_handler)&lt;br /&gt;
   user_field(flnk3, iocStringT,  &amp;quot;recordC&amp;quot;, flnk_handler)&lt;br /&gt;
   # In these examples, the PARM is the initial value of the field.&lt;br /&gt;
   # But it could be any string that's used to configure the handler:&lt;br /&gt;
   # user_field(syslog, iocStringT, &amp;quot;server=124.0.0.5:9876,interval=1&amp;quot;, syslog_handler)&lt;br /&gt;
 }      &lt;br /&gt;
 */&lt;br /&gt;
 int main()&lt;br /&gt;
 {&lt;br /&gt;
     // This is sort of what would need to happen inside dbLoadDatabase().&lt;br /&gt;
     // The tricky part is the &amp;quot;new xxx_handler&amp;quot;:&lt;br /&gt;
     // Each handler will have to use the registry to register a factory&lt;br /&gt;
     // method so that dbLoadDatabase can invoke &amp;quot;new xxx_handler&amp;quot; without&lt;br /&gt;
     // recompilation.&lt;br /&gt;
     record fred(&amp;quot;fred&amp;quot;);&lt;br /&gt;
     fred.add_user_field(&amp;quot;smoo&amp;quot;, iocFloat64T, &amp;quot;0.5&amp;quot;,     new smoo_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;max&amp;quot;,  iocFloat64T, &amp;quot;&amp;quot;,        new max_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;flnk1&amp;quot;, iocStringT, &amp;quot;recordA&amp;quot;, new flnk_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;flnk2&amp;quot;, iocStringT, &amp;quot;recordB&amp;quot;, new flnk_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;flnk3&amp;quot;, iocStringT, &amp;quot;recordC&amp;quot;, new flnk_handler());&lt;br /&gt;
     //&lt;br /&gt;
     fred.iocInit();&lt;br /&gt;
     //&lt;br /&gt;
     fred.process();&lt;br /&gt;
     cout &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
     fred.process();&lt;br /&gt;
     &lt;br /&gt;
     return 0;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 /*&lt;br /&gt;
 When run, this basically processes the record and the&lt;br /&gt;
 code for the user-defined fields is invoked as intended:&lt;br /&gt;
 &lt;br /&gt;
 record::iocInit(fred)&lt;br /&gt;
 record::process(fred)&lt;br /&gt;
 Smoothing by 0.5&lt;br /&gt;
 Max is 0&lt;br /&gt;
 FLNKing to recordA&lt;br /&gt;
 FLNKing to recordB&lt;br /&gt;
 FLNKing to recordC&lt;br /&gt;
 &lt;br /&gt;
 record::process(fred)&lt;br /&gt;
 Smoothing by 0.5&lt;br /&gt;
 Max is 0&lt;br /&gt;
 FLNKing to recordA&lt;br /&gt;
 FLNKing to recordB&lt;br /&gt;
 FLNKing to recordC&lt;br /&gt;
 &lt;br /&gt;
 UNCLEAR:&lt;br /&gt;
 Is string parm enough, or are structs requires?&lt;br /&gt;
 &lt;br /&gt;
 I believe that all the goals 1-7 can be handled&lt;br /&gt;
 by providing the appropriate hooks.&lt;br /&gt;
 Question: How many hooks will that be?&lt;br /&gt;
   &lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Comments ==&lt;br /&gt;
The example code for user_field_handler::init() &lt;br /&gt;
assumes that the record has&lt;br /&gt;
a routine  get_field_addr(string name) that provides a raw pointer&lt;br /&gt;
to each field of a record. Would have to use dataAccess or whatever the&lt;br /&gt;
runtime database will ultimately provide.&lt;br /&gt;
&lt;br /&gt;
Some pro and cons when comparing this type of user-defined field to writing a custom record:&lt;br /&gt;
&lt;br /&gt;
=== Pro ===&lt;br /&gt;
* One can add fields per record instance as needed. No need to create a possibly confusing plethora of specialized record types:  BasicAI, MaxAI, AverageAI, AverageAndMaxAI, BasicAO, MaxAO, ...&lt;br /&gt;
* Under vxWorks, one can do so without recompilation:&lt;br /&gt;
   ld &amp;lt;smoo_handlerLib.o&lt;br /&gt;
   dbLoadDataBase(&amp;quot;..../records_that_use_smoo_handler&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
=== Contra ===&lt;br /&gt;
* The actions inside a user-field handler are limited to quick, synchronous actions.&lt;br /&gt;
* In order to handle the goals mentioned in example.cpp, only two hooks into the record's process() routine are needed. Unclear if this is sufficient in the long run.&lt;br /&gt;
* While easy to use, the code requires more runtime checks, more type casts, more boilerplate code. For example, the smoo_handler should react accordingly if the record does not have a numeric value field. In a custom record type, compile-time checks would reduce the code size and increase maintainabililty.&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_User-defined_fields&amp;diff=433</id>
		<title>V4 User-defined fields</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_User-defined_fields&amp;diff=433"/>
		<updated>2005-07-14T22:27:08Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: /* Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
The 'Vampire' (see [[V4_Server_Side_Plugins]]) seems to handle all but the forward link,&lt;br /&gt;
and seems an overall better idea.&lt;br /&gt;
&lt;br /&gt;
Records, the building blocks of an EPICS run-time database,&lt;br /&gt;
are much more powerful than the fundamental commands&lt;br /&gt;
of programming languages like C, C++, LabVIEW or PLC&lt;br /&gt;
ladder logic: A record like the AI record provides scanning,&lt;br /&gt;
various conversions, alarms, smoothing and more.&lt;br /&gt;
&lt;br /&gt;
On the downside, very few record instances use the full functionality&lt;br /&gt;
of the record type. Extending their functionality is rather involved,&lt;br /&gt;
and very few custom record types get shared in the community.&lt;br /&gt;
&lt;br /&gt;
If it was easier to create, modify and extend records,&lt;br /&gt;
we could start with simpler record types and extend them&lt;br /&gt;
as needed.&lt;br /&gt;
&lt;br /&gt;
This is a suggestion for supporting user-defined fields in V4,&lt;br /&gt;
allowing the user to add fields to a record instance without&lt;br /&gt;
first having to create a new record type.&lt;br /&gt;
There is an alternate proposal,&lt;br /&gt;
[[V4 Design: Assembling Record Support]],&lt;br /&gt;
that would simplify the creation of new record types.&lt;br /&gt;
&lt;br /&gt;
== User-defined fields ==&lt;br /&gt;
&lt;br /&gt;
For example, I'd like to allow users to define &amp;quot;analog input&amp;quot; records&lt;br /&gt;
with the usual fields '''and''' allow them to add new fields to some&lt;br /&gt;
instance without having to create a new record type:&lt;br /&gt;
&lt;br /&gt;
 calc(&amp;quot;fred&amp;quot;)&lt;br /&gt;
 {&lt;br /&gt;
    field(SCAN, &amp;quot;1 second&amp;quot;)&lt;br /&gt;
    user_field(SMOO,  iocFloat64T, &amp;quot;0.5&amp;quot;, smoo_handler)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This record will have a 'SMOO' field of type double (float 64) that presumably implements&lt;br /&gt;
smoothing in the user-supplied smoo_handler.&lt;br /&gt;
&lt;br /&gt;
The idea is that one can add these user-defined fields on a per-instance basis&lt;br /&gt;
to pretty much any record type simply by editing the DB file and loading the&lt;br /&gt;
handler code, without having to recompile the rest of EPICS base.&lt;br /&gt;
&lt;br /&gt;
Example code for a ''handler'' and what the ''database'' code would have to do&lt;br /&gt;
follows.&lt;br /&gt;
&lt;br /&gt;
=== database.h ===&lt;br /&gt;
&lt;br /&gt;
 #include &amp;lt;iostream&amp;gt;&lt;br /&gt;
 #include &amp;lt;string&amp;gt;&lt;br /&gt;
 #include &amp;lt;list&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 using namespace std;&lt;br /&gt;
 &lt;br /&gt;
 enum iocType&lt;br /&gt;
 {&lt;br /&gt;
     iocUnknownT,iocBooleanT,iocOctetT,&lt;br /&gt;
     iocInt16T,iocUInt16T,iocInt32T,iocUInt32T,iocInt64T,iocUInt64T,&lt;br /&gt;
     iocFloat32T,iocFloat64T,&lt;br /&gt;
     iocStringT,iocMenuT,iocEnumT,iocLinkT,iocDeviceT,iocArrayT&lt;br /&gt;
 };&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // Support for user-defined fields in base&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // forwards&lt;br /&gt;
 class record;&lt;br /&gt;
 class user_field_handler;&lt;br /&gt;
 &lt;br /&gt;
 // Information for one user-defined field&lt;br /&gt;
 class user_field_data&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     string  name;&lt;br /&gt;
     iocType type;&lt;br /&gt;
     void    *data;&lt;br /&gt;
     string  parm;&lt;br /&gt;
     user_field_handler *handler;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // The 'handler' specified for a user field needs to implement&lt;br /&gt;
 // this interface:&lt;br /&gt;
 class user_field_handler&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual void init(user_field_data *field, record *rec)     {}&lt;br /&gt;
     virtual void update(user_field_data *field, record *rec)   {}&lt;br /&gt;
     virtual void destroy(user_field_data *field, record *rec)  {}  &lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // Records need to support hooks similar to Java 'listeners'&lt;br /&gt;
 // so that user fields can hook into record processing.&lt;br /&gt;
 // Unclear at what levels hooks are needed.&lt;br /&gt;
 class got_data_hook&lt;br /&gt;
 {&lt;br /&gt;
 public:    virtual void run_got_data_hook()=0;&lt;br /&gt;
 };&lt;br /&gt;
 class post_monitor_hook&lt;br /&gt;
 {&lt;br /&gt;
 public:    virtual void run_post_monitor_hook()=0;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // Pieces of record code in order to show where the hooks&lt;br /&gt;
 // are handled inside of 'process()'&lt;br /&gt;
 class record&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     record(string name) : name(name) {}&lt;br /&gt;
     &lt;br /&gt;
     void add_user_field(string name, iocType type, string parm,&lt;br /&gt;
                         user_field_handler *handler)&lt;br /&gt;
     {&lt;br /&gt;
         user_field_data ufd;&lt;br /&gt;
         ufd.name = name;&lt;br /&gt;
         ufd.type = type;&lt;br /&gt;
         ufd.parm = parm;&lt;br /&gt;
         ufd.handler = handler;&lt;br /&gt;
         user_fields.push_back(ufd);&lt;br /&gt;
     }&lt;br /&gt;
     void add_got_data_hook(got_data_hook *hook)&lt;br /&gt;
     {&lt;br /&gt;
         got_data_hooks.push_back(hook);&lt;br /&gt;
     }&lt;br /&gt;
     // TODO: remove_got_data_hook();&lt;br /&gt;
     void add_post_monitor_hook(post_monitor_hook *hook)&lt;br /&gt;
     {&lt;br /&gt;
         post_monitor_hooks.push_back(hook);&lt;br /&gt;
     }&lt;br /&gt;
     // TODO: remove_post_monitor_hook();&lt;br /&gt;
 &lt;br /&gt;
     void iocInit();&lt;br /&gt;
 &lt;br /&gt;
     void process();&lt;br /&gt;
 private:&lt;br /&gt;
     string                     name;&lt;br /&gt;
     list&amp;lt;user_field_data&amp;gt;      user_fields;&lt;br /&gt;
     list&amp;lt;got_data_hook *&amp;gt;      got_data_hooks;&lt;br /&gt;
     list&amp;lt;post_monitor_hook *&amp;gt;  post_monitor_hooks;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== database.cpp ===&lt;br /&gt;
&lt;br /&gt;
 #include &amp;quot;database.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 void *get_iocType_mem(iocType type)&lt;br /&gt;
 {&lt;br /&gt;
     switch (type)&lt;br /&gt;
     {&lt;br /&gt;
         case iocFloat32T: return new float;&lt;br /&gt;
         case iocFloat64T: return new double;&lt;br /&gt;
         case iocStringT:  return new string;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 void record::iocInit()&lt;br /&gt;
 {&lt;br /&gt;
     cout &amp;lt;&amp;lt; &amp;quot;record::iocInit(&amp;quot; &amp;lt;&amp;lt; name &amp;lt;&amp;lt; &amp;quot;)\n&amp;quot;;&lt;br /&gt;
     list&amp;lt;user_field_data&amp;gt;::iterator ufi;&lt;br /&gt;
     for (ufi = user_fields.begin();  ufi != user_fields.end();  ++ufi)&lt;br /&gt;
     {&lt;br /&gt;
         if ((ufi-&amp;gt;data = get_iocType_mem(ufi-&amp;gt;type)) != 0)&lt;br /&gt;
             ufi-&amp;gt;handler-&amp;gt;init(&amp;amp;(*ufi), this);&lt;br /&gt;
         else&lt;br /&gt;
             ufi-&amp;gt;type = iocUnknownT;&lt;br /&gt;
     }   &lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 void record::process()&lt;br /&gt;
 {&lt;br /&gt;
     cout &amp;lt;&amp;lt; &amp;quot;record::process(&amp;quot; &amp;lt;&amp;lt; name &amp;lt;&amp;lt; &amp;quot;)\n&amp;quot;;&lt;br /&gt;
 #ifdef TODO&lt;br /&gt;
     status=readValue(pai); /* read the new value */&lt;br /&gt;
     if ( !pact &amp;amp;&amp;amp; pai-&amp;gt;pact ) return(0);&lt;br /&gt;
     pai-&amp;gt;pact = TRUE;&lt;br /&gt;
     &lt;br /&gt;
     recGblGetTimeStamp(pai);&lt;br /&gt;
     if (status==0) convert(pai);&lt;br /&gt;
     else if (status==2) status=0;&lt;br /&gt;
 #endif&lt;br /&gt;
     list&amp;lt;got_data_hook *&amp;gt;::iterator gdhi;&lt;br /&gt;
     for (gdhi = got_data_hooks.begin(); gdhi != got_data_hooks.end(); ++gdhi)&lt;br /&gt;
         (*gdhi)-&amp;gt;run_got_data_hook();&lt;br /&gt;
 #ifdef TODO&lt;br /&gt;
     checkAlarms(pai);&lt;br /&gt;
     monitor(pai);&lt;br /&gt;
 #endif&lt;br /&gt;
     list&amp;lt;post_monitor_hook *&amp;gt;::iterator pmhi;&lt;br /&gt;
     for (pmhi=post_monitor_hooks.begin(); pmhi!=post_monitor_hooks.end(); ++pmhi)&lt;br /&gt;
         (*pmhi)-&amp;gt;run_post_monitor_hook();&lt;br /&gt;
 #ifdef TODO&lt;br /&gt;
     pai-&amp;gt;pact=FALSE;&lt;br /&gt;
 #endif&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // TODO:&lt;br /&gt;
 // Record will need to check all it's user_fields[].name&lt;br /&gt;
 // in case CA tries to get/put.&lt;br /&gt;
&lt;br /&gt;
=== example.cpp ===&lt;br /&gt;
&lt;br /&gt;
 /* Goals:&lt;br /&gt;
 - Avg or other statistical calc:&lt;br /&gt;
   deposits calculated data into new fields,&lt;br /&gt;
   doesn't affect the rest of the record.&lt;br /&gt;
 - Circ Buffer (archive record):&lt;br /&gt;
   Similar, does something with the current VAL&lt;br /&gt;
   outside of the record.&lt;br /&gt;
 - Put Logging: Simlar.&lt;br /&gt;
 - SMOO: Can it modify the VAL field?&lt;br /&gt;
 - BPM combiner of 4 AIs: Unclear.&lt;br /&gt;
 - conversions (new breakpt. table): unclear.&lt;br /&gt;
 - FLNK (w/ delays?): Should happen _after_ all else is done.&lt;br /&gt;
 &lt;br /&gt;
 The following implements handlers for some of the above.&lt;br /&gt;
 &lt;br /&gt;
 */&lt;br /&gt;
 #include &amp;quot;database.h&amp;quot;&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // MAX (easier to calc. then e.g. running average&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 class max_handler : public user_field_handler, got_data_hook&lt;br /&gt;
 {&lt;br /&gt;
     void init(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         max = (double *)field-&amp;gt;data;&lt;br /&gt;
         // TODO: val = record-&amp;gt;get_field_addr(&amp;quot;VAL&amp;quot;)&lt;br /&gt;
         rec-&amp;gt;add_got_data_hook(this);&lt;br /&gt;
     }&lt;br /&gt;
     void run_got_data_hook()&lt;br /&gt;
     {&lt;br /&gt;
         // if (*val &amp;gt; *max)   *max = *val;&lt;br /&gt;
         cout &amp;lt;&amp;lt; &amp;quot;Max is &amp;quot; &amp;lt;&amp;lt; *max &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     double *max;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // SMOO&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 class smoo_handler : public user_field_handler, got_data_hook&lt;br /&gt;
 {&lt;br /&gt;
     void init(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         smoo = (double *)field-&amp;gt;data;&lt;br /&gt;
         *smoo = atof(field-&amp;gt;parm.c_str());&lt;br /&gt;
         // TODO: val = record-&amp;gt;get_field_addr(&amp;quot;VAL&amp;quot;)&lt;br /&gt;
         rec-&amp;gt;add_got_data_hook(this);&lt;br /&gt;
     }&lt;br /&gt;
     void run_got_data_hook()&lt;br /&gt;
     {&lt;br /&gt;
         // *val = smoo*(*val) + (1-smoo)*prev_value;&lt;br /&gt;
         // prev_value = *val;&lt;br /&gt;
         cout &amp;lt;&amp;lt; &amp;quot;Smoothing by &amp;quot; &amp;lt;&amp;lt; *smoo &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     double *smoo;&lt;br /&gt;
     double prev_value;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // FLNK&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 class flnk_handler : public user_field_handler, post_monitor_hook&lt;br /&gt;
 {&lt;br /&gt;
     void init(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         link_text = (string *)field-&amp;gt;data;&lt;br /&gt;
         *link_text = field-&amp;gt;parm;&lt;br /&gt;
         update(field, rec);&lt;br /&gt;
         rec-&amp;gt;add_post_monitor_hook(this);&lt;br /&gt;
     }&lt;br /&gt;
     void update(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         // TODO: ink_info = parse_link_info_from_string(*link_text);&lt;br /&gt;
         link_info = *link_text;&lt;br /&gt;
     }&lt;br /&gt;
     void run_post_monitor_hook()&lt;br /&gt;
     {&lt;br /&gt;
         cout &amp;lt;&amp;lt; &amp;quot;FLNKing to &amp;quot; &amp;lt;&amp;lt; link_info &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
         // TODO: dbProcessLink(this-&amp;gt;link_info);&lt;br /&gt;
         // Or with additional delay parm:&lt;br /&gt;
         // wdStart(delay, dbProcessLink, this-&amp;gt;link_info);  ?&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     string *link_text;&lt;br /&gt;
     string link_info;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // Example database snippet and how it's used&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 /*&lt;br /&gt;
 ai(&amp;quot;fred&amp;quot;)&lt;br /&gt;
 {&lt;br /&gt;
   #          NAME, TYPE,         PARM,      HANDLER&lt;br /&gt;
   user_field(smoo,  iocFloat64T, &amp;quot;0.5&amp;quot;,     smoo_handler)&lt;br /&gt;
   user_field(max,   iocFloat64T, &amp;quot;&amp;quot;,        max_handler)&lt;br /&gt;
   user_field(flnk1, iocStringT,  &amp;quot;recordA&amp;quot;, flnk_handler)&lt;br /&gt;
   user_field(flnk2, iocStringT,  &amp;quot;recordB&amp;quot;, flnk_handler)&lt;br /&gt;
   user_field(flnk3, iocStringT,  &amp;quot;recordC&amp;quot;, flnk_handler)&lt;br /&gt;
   # In these examples, the PARM is the initial value of the field.&lt;br /&gt;
   # But it could be any string that's used to configure the handler:&lt;br /&gt;
   # user_field(syslog, iocStringT, &amp;quot;server=124.0.0.5:9876,interval=1&amp;quot;, syslog_handler)&lt;br /&gt;
 }      &lt;br /&gt;
 */&lt;br /&gt;
 int main()&lt;br /&gt;
 {&lt;br /&gt;
     // This is sort of what would need to happen inside dbLoadDatabase().&lt;br /&gt;
     // The tricky part is the &amp;quot;new xxx_handler&amp;quot;:&lt;br /&gt;
     // Each handler will have to use the registry to register a factory&lt;br /&gt;
     // method so that dbLoadDatabase can invoke &amp;quot;new xxx_handler&amp;quot; without&lt;br /&gt;
     // recompilation.&lt;br /&gt;
     record fred(&amp;quot;fred&amp;quot;);&lt;br /&gt;
     fred.add_user_field(&amp;quot;smoo&amp;quot;, iocFloat64T, &amp;quot;0.5&amp;quot;,     new smoo_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;max&amp;quot;,  iocFloat64T, &amp;quot;&amp;quot;,        new max_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;flnk1&amp;quot;, iocStringT, &amp;quot;recordA&amp;quot;, new flnk_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;flnk2&amp;quot;, iocStringT, &amp;quot;recordB&amp;quot;, new flnk_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;flnk3&amp;quot;, iocStringT, &amp;quot;recordC&amp;quot;, new flnk_handler());&lt;br /&gt;
     //&lt;br /&gt;
     fred.iocInit();&lt;br /&gt;
     //&lt;br /&gt;
     fred.process();&lt;br /&gt;
     cout &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
     fred.process();&lt;br /&gt;
     &lt;br /&gt;
     return 0;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 /*&lt;br /&gt;
 When run, this basically processes the record and the&lt;br /&gt;
 code for the user-defined fields is invoked as intended:&lt;br /&gt;
 &lt;br /&gt;
 record::iocInit(fred)&lt;br /&gt;
 record::process(fred)&lt;br /&gt;
 Smoothing by 0.5&lt;br /&gt;
 Max is 0&lt;br /&gt;
 FLNKing to recordA&lt;br /&gt;
 FLNKing to recordB&lt;br /&gt;
 FLNKing to recordC&lt;br /&gt;
 &lt;br /&gt;
 record::process(fred)&lt;br /&gt;
 Smoothing by 0.5&lt;br /&gt;
 Max is 0&lt;br /&gt;
 FLNKing to recordA&lt;br /&gt;
 FLNKing to recordB&lt;br /&gt;
 FLNKing to recordC&lt;br /&gt;
 &lt;br /&gt;
 UNCLEAR:&lt;br /&gt;
 Is string parm enough, or are structs requires?&lt;br /&gt;
 &lt;br /&gt;
 I believe that all the goals 1-7 can be handled&lt;br /&gt;
 by providing the appropriate hooks.&lt;br /&gt;
 Question: How many hooks will that be?&lt;br /&gt;
   &lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Comments ==&lt;br /&gt;
The example code for user_field_handler::init() &lt;br /&gt;
assumes that the record has&lt;br /&gt;
a routine  get_field_addr(string name) that provides a raw pointer&lt;br /&gt;
to each field of a record. Would have to use dataAccess or whatever the&lt;br /&gt;
runtime database will ultimately provide.&lt;br /&gt;
&lt;br /&gt;
Some pro and cons when comparing this type of user-defined field to writing a custom record:&lt;br /&gt;
&lt;br /&gt;
=== Pro ===&lt;br /&gt;
* One can add fields per record instance as needed. No need to create a possibly confusing plethora of specialized record types:  BasicAI, MaxAI, AverageAI, AverageAndMaxAI, BasicAO, MaxAO, ...&lt;br /&gt;
* Under vxWorks, one can do so without recompilation:&lt;br /&gt;
   ld &amp;lt;smoo_handlerLib.o&lt;br /&gt;
   dbLoadDataBase(&amp;quot;..../records_that_use_smoo_handler&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
=== Contra ===&lt;br /&gt;
* The actions inside a user-field handler are limited to quick, synchronous actions.&lt;br /&gt;
* In order to handle the goals mentioned in example.cpp, only two hooks into the record's process() routine are needed. Unclear if this is sufficient in the long run.&lt;br /&gt;
* While easy to use, the code requires more runtime checks, more type casts, more boilerplate code. For example, the smoo_handler should react accordingly if the record does not have a numeric value field. In a custom record type, compile-time checks would reduce the code size and increase maintainabililty.&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=434</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=434"/>
		<updated>2005-07-14T20:36:39Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: /* Data Access */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Constraints ==&lt;br /&gt;
* Initial implementation will be in C++&lt;br /&gt;
* Should very easily translate into Java, just like e.g. some XML APIs use the exact same classes and methods in C++ and Java. Ideally, Java and C++ version will be available from the start.&lt;br /&gt;
* Ideally, the C++ headers would be simple enough to be parsed by SWIG and hence perl/tcl/python/... glue code could be auto-generated via SWIG&lt;br /&gt;
* API is of course specific to EPICS CA, but ideally it'll also be usable as an interface to other protocols, so that EPICS Office can use an API that is only a slight extension of the CA client user API.&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. The existing dataAccess interface is based on&lt;br /&gt;
a visitor pattern. The user cannot list the available properties nor pick&lt;br /&gt;
a selected property. Instead, the user can request complete traversal&lt;br /&gt;
or a callback for a specific property.&lt;br /&gt;
&lt;br /&gt;
This might be the most efficient implementation, but not necessarily&lt;br /&gt;
the one that's easiest to understand. The user interface to dataAccess&lt;br /&gt;
should thus also provide the following convenience routines:&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime. To avoid security issues, setUser will probably send a public key and not a user name and password.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion, response})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value (delivery) or  all the processing triggered by the new value has completed (completion). In addition, some records might support command/response, in which case one can write a command and wait for a response callback which looks just like the result of a read request.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercentageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== ChannelGroup ===&lt;br /&gt;
A channel group synchronizes access to multiple channels.&lt;br /&gt;
It listens to all members of the channel group and for example&lt;br /&gt;
allows waiting for ''all'' outstanding read or write requests&lt;br /&gt;
to return.&lt;br /&gt;
&lt;br /&gt;
* add/remove channels&amp;lt;br&amp;gt;By name? Already existing Channel? Add channels that match a pattern, or go via the directory?&lt;br /&gt;
* read/write&amp;lt;br&amp;gt;Has to handle reading/writing the same properties from all channels in group. Also channel-specific list of properties?&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
Also see [[V4 Data Store]]&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
We can imagine an even simpler example:&lt;br /&gt;
&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of fred:&amp;quot; &amp;lt;&amp;lt; Epics::get(&amp;quot;fred&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
This implies lots of work under the hood, with reasonable defaults.&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=423</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=423"/>
		<updated>2005-07-14T20:19:17Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: /* Channel: Writing */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Constraints ==&lt;br /&gt;
* Initial implementation will be in C++&lt;br /&gt;
* Should very easily translate into Java, just like e.g. some XML APIs use the exact same classes and methods in C++ and Java. Ideally, Java and C++ version will be available from the start.&lt;br /&gt;
* Ideally, the C++ headers would be simple enough to be parsed by SWIG and hence perl/tcl/python/... glue code could be auto-generated via SWIG&lt;br /&gt;
* API is of course specific to EPICS CA, but ideally it'll also be usable as an interface to other protocols, so that EPICS Office can use an API that is only a slight extension of the CA client user API.&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime. To avoid security issues, setUser will probably send a public key and not a user name and password.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion, response})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value (delivery) or  all the processing triggered by the new value has completed (completion). In addition, some records might support command/response, in which case one can write a command and wait for a response callback which looks just like the result of a read request.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercentageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== ChannelGroup ===&lt;br /&gt;
A channel group synchronizes access to multiple channels.&lt;br /&gt;
It listens to all members of the channel group and for example&lt;br /&gt;
allows waiting for ''all'' outstanding read or write requests&lt;br /&gt;
to return.&lt;br /&gt;
&lt;br /&gt;
* add/remove channels&amp;lt;br&amp;gt;By name? Already existing Channel? Add channels that match a pattern, or go via the directory?&lt;br /&gt;
* read/write&amp;lt;br&amp;gt;Has to handle reading/writing the same properties from all channels in group. Also channel-specific list of properties?&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
Also see [[V4 Data Store]]&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
We can imagine an even simpler example:&lt;br /&gt;
&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of fred:&amp;quot; &amp;lt;&amp;lt; Epics::get(&amp;quot;fred&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
This implies lots of work under the hood, with reasonable defaults.&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=422</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=422"/>
		<updated>2005-07-14T17:52:24Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Constraints ==&lt;br /&gt;
* Initial implementation will be in C++&lt;br /&gt;
* Should very easily translate into Java, just like e.g. some XML APIs use the exact same classes and methods in C++ and Java. Ideally, Java and C++ version will be available from the start.&lt;br /&gt;
* Ideally, the C++ headers would be simple enough to be parsed by SWIG and hence perl/tcl/python/... glue code could be auto-generated via SWIG&lt;br /&gt;
* API is of course specific to EPICS CA, but ideally it'll also be usable as an interface to other protocols, so that EPICS Office can use an API that is only a slight extension of the CA client user API.&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime. To avoid security issues, setUser will probably send a public key and not a user name and password.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercentageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== ChannelGroup ===&lt;br /&gt;
A channel group synchronizes access to multiple channels.&lt;br /&gt;
It listens to all members of the channel group and for example&lt;br /&gt;
allows waiting for ''all'' outstanding read or write requests&lt;br /&gt;
to return.&lt;br /&gt;
&lt;br /&gt;
* add/remove channels&amp;lt;br&amp;gt;By name? Already existing Channel? Add channels that match a pattern, or go via the directory?&lt;br /&gt;
* read/write&amp;lt;br&amp;gt;Has to handle reading/writing the same properties from all channels in group. Also channel-specific list of properties?&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
Also see [[V4 Data Store]]&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
We can imagine an even simpler example:&lt;br /&gt;
&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of fred:&amp;quot; &amp;lt;&amp;lt; Epics::get(&amp;quot;fred&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
This implies lots of work under the hood, with reasonable defaults.&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=420</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=420"/>
		<updated>2005-07-14T17:50:35Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Constraints&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Constraints ==&lt;br /&gt;
* Initial implementation will be in C++&lt;br /&gt;
* Should very easily translate into Java, just like e.g. some XML APIs use the exact same classes and methods in C++ and Java. Ideally, Java and C++ version will be available from the start.&lt;br /&gt;
* Ideally, the C++ headers would be simple enough to be parsed by SWIG and hence perl/tcl/python/... glue code could be auto-generated via SWIG&lt;br /&gt;
&lt;br /&gt;
== Constraints ==&lt;br /&gt;
* Initial implementation will be in C++&lt;br /&gt;
* Should very easily translate into Java, just like e.g. some XML APIs use the exact same classes and methods in C++ and Java. Ideally, Java and C++ version will be available from the start.&lt;br /&gt;
* Ideally, the C++ headers would be simple enough to be parsed by SWIG and hence perl/tcl/python/... glue code could be auto-generated via SWIG&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime. To avoid security issues, setUser will probably send a public key and not a user name and password.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercentageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== ChannelGroup ===&lt;br /&gt;
A channel group synchronizes access to multiple channels.&lt;br /&gt;
It listens to all members of the channel group and for example&lt;br /&gt;
allows waiting for ''all'' outstanding read or write requests&lt;br /&gt;
to return.&lt;br /&gt;
&lt;br /&gt;
* add/remove channels&amp;lt;br&amp;gt;By name? Already existing Channel? Add channels that match a pattern, or go via the directory?&lt;br /&gt;
* read/write&amp;lt;br&amp;gt;Has to handle reading/writing the same properties from all channels in group. Also channel-specific list of properties?&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
Also see [[V4 Data Store]]&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
We can imagine an even simpler example:&lt;br /&gt;
&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of fred:&amp;quot; &amp;lt;&amp;lt; Epics::get(&amp;quot;fred&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
This implies lots of work under the hood, with reasonable defaults.&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=419</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=419"/>
		<updated>2005-07-14T17:50:30Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Constraints&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Constraints ==&lt;br /&gt;
* Initial implementation will be in C++&lt;br /&gt;
* Should very easily translate into Java, just like e.g. some XML APIs use the exact same classes and methods in C++ and Java. Ideally, Java and C++ version will be available from the start.&lt;br /&gt;
* Ideally, the C++ headers would be simple enough to be parsed by SWIG and hence perl/tcl/python/... glue code could be auto-generated via SWIG&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime. To avoid security issues, setUser will probably send a public key and not a user name and password.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercentageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== ChannelGroup ===&lt;br /&gt;
A channel group synchronizes access to multiple channels.&lt;br /&gt;
It listens to all members of the channel group and for example&lt;br /&gt;
allows waiting for ''all'' outstanding read or write requests&lt;br /&gt;
to return.&lt;br /&gt;
&lt;br /&gt;
* add/remove channels&amp;lt;br&amp;gt;By name? Already existing Channel? Add channels that match a pattern, or go via the directory?&lt;br /&gt;
* read/write&amp;lt;br&amp;gt;Has to handle reading/writing the same properties from all channels in group. Also channel-specific list of properties?&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
Also see [[V4 Data Store]]&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
We can imagine an even simpler example:&lt;br /&gt;
&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of fred:&amp;quot; &amp;lt;&amp;lt; Epics::get(&amp;quot;fred&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
This implies lots of work under the hood, with reasonable defaults.&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=418</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=418"/>
		<updated>2005-07-13T20:38:58Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: /* ChannelGroup */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime. To avoid security issues, setUser will probably send a public key and not a user name and password.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercentageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== ChannelGroup ===&lt;br /&gt;
A channel group synchronizes access to multiple channels.&lt;br /&gt;
It listens to all members of the channel group and for example&lt;br /&gt;
allows waiting for ''all'' outstanding read or write requests&lt;br /&gt;
to return.&lt;br /&gt;
&lt;br /&gt;
* add/remove channels&amp;lt;br&amp;gt;By name? Already existing Channel? Add channels that match a pattern, or go via the directory?&lt;br /&gt;
* read/write&amp;lt;br&amp;gt;Has to handle reading/writing the same properties from all channels in group. Also channel-specific list of properties?&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
Also see [[V4 Data Store]]&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
We can imagine an even simpler example:&lt;br /&gt;
&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of fred:&amp;quot; &amp;lt;&amp;lt; Epics::get(&amp;quot;fred&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
This implies lots of work under the hood, with reasonable defaults.&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=404</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=404"/>
		<updated>2005-07-13T20:23:35Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: /* Channel: State */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime. To avoid security issues, setUser will probably send a public key and not a user name and password.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercentageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== ChannelGroup ===&lt;br /&gt;
&lt;br /&gt;
A channel group synchronizes access to multiple channels.&lt;br /&gt;
It listens to all members of the channel group and for example&lt;br /&gt;
allows waiting for ''all'' outstanding read or write requests&lt;br /&gt;
to return.&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
Also see [[V4 Data Store]]&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
We can imagine an even simpler example:&lt;br /&gt;
&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of fred:&amp;quot; &amp;lt;&amp;lt; Epics::get(&amp;quot;fred&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
This implies lots of work under the hood, with reasonable defaults.&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=403</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=403"/>
		<updated>2005-07-13T20:22:30Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: /* SyncChannel */ , ChannelGroup&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercentageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== ChannelGroup ===&lt;br /&gt;
&lt;br /&gt;
A channel group synchronizes access to multiple channels.&lt;br /&gt;
It listens to all members of the channel group and for example&lt;br /&gt;
allows waiting for ''all'' outstanding read or write requests&lt;br /&gt;
to return.&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
Also see [[V4 Data Store]]&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
We can imagine an even simpler example:&lt;br /&gt;
&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of fred:&amp;quot; &amp;lt;&amp;lt; Epics::get(&amp;quot;fred&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
This implies lots of work under the hood, with reasonable defaults.&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=402</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=402"/>
		<updated>2005-07-13T20:20:33Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: /* Channel: Reading */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercentageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
Also see [[V4 Data Store]]&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
We can imagine an even simpler example:&lt;br /&gt;
&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of fred:&amp;quot; &amp;lt;&amp;lt; Epics::get(&amp;quot;fred&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
This implies lots of work under the hood, with reasonable defaults.&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=372</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=372"/>
		<updated>2005-07-07T17:06:23Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: no connect, thus no disconnect; Event from info to interface; camonitor&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; properties, Event e)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
* cancelSubscription(....)&amp;lt;BR&amp;gt;Use properties &amp;amp; event or an ID returned from createSubscription?&lt;br /&gt;
&lt;br /&gt;
Event is a class that allows the user to select what triggers an update:&lt;br /&gt;
* useDefaultValueDeadband()&amp;lt;BR&amp;gt;Default if nothing else is called.&lt;br /&gt;
* setValueDeadband(double change)&lt;br /&gt;
* setPercantageDeadband(double percent)&lt;br /&gt;
* setLogarithmicDeadband(double exponent_of_ten)&lt;br /&gt;
* setMinimumRate(double seconds)&amp;lt;BR&amp;gt;There will be notifications at at least this rate, even if value didn't change.&lt;br /&gt;
* setMaximumRate(double vals_per_second)&lt;br /&gt;
* useAlarmConditionChange()&lt;br /&gt;
* useHardwareEvent(int event_id)&amp;lt;br&amp;gt;Somehow, event_id might be linked to 'Blue Beam'&lt;br /&gt;
* setMaxValueCount&amp;lt;br&amp;gt;Setting this to '1' turns the subscription into a single-value 'get'.&lt;br /&gt;
&lt;br /&gt;
Unclear how to present the Event to a user. For example, how would a 'camonitor' tool allow the user to select the events? Via 20 command-line switches, or should there be a syntax ala&lt;br /&gt;
&amp;quot;valueDeadband=1.0, maximumRate=20&amp;quot; which is supported by methods&lt;br /&gt;
* string toString()&lt;br /&gt;
* bood fromString(string event_specification)&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
== camonitor 101 ==&lt;br /&gt;
This is the goal for a 'camonitor':&lt;br /&gt;
&lt;br /&gt;
 class MyMonitor : public Channel::Listener&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
   newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
   {&lt;br /&gt;
        cout &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
 };&lt;br /&gt;
 ...&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 list&amp;lt;string&amp;gt; properties;&lt;br /&gt;
 properties.push_back(&amp;quot;value&amp;quot;);&lt;br /&gt;
 Event e;&lt;br /&gt;
 e. setMaximumRate(10.0);&lt;br /&gt;
 channel.createSubscription(properties, e);&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=362</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=362"/>
		<updated>2005-07-07T16:47:40Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Had the 'read' misplaced&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* disconnected(Channel ch)&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; property, Event e, Filter f)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
** event is a set of any of the following:&lt;br /&gt;
*** exceeded absolute value deadband, exceeded percentage change, exceeded logarithmic change&lt;br /&gt;
*** minimum update period expired&lt;br /&gt;
*** alarm condition change&lt;br /&gt;
*** some hardware event (like blue beam)&lt;br /&gt;
** filters&lt;br /&gt;
*** override server-side idea of the value deadband, percentage change, log. change, minumum update rate, maximum update rate&lt;br /&gt;
*** specify value count&amp;lt;br&amp;gt;setting this to '1' turns the subscription into a single-value 'get'&lt;br /&gt;
* cancelSubscription(Event, Filter)&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read'.&lt;br /&gt;
&lt;br /&gt;
* Inherits from Channel.&lt;br /&gt;
* double getTimeout(), void setTimeout(double seconds)&lt;br /&gt;
* DataAccess read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* DataAccess read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=361</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=361"/>
		<updated>2005-07-07T16:42:57Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Hint to Marty's prop. handling idea. Had dropped the basic 'read'.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
Marty has a more complete idea, where a proterty catalog converts strings to IDs&lt;br /&gt;
and then those are actually used to access the data:&lt;br /&gt;
&lt;br /&gt;
 struct Property {&lt;br /&gt;
    epicsInt32 id;&lt;br /&gt;
    dbfType    type;&lt;br /&gt;
    void       *pdbf;&lt;br /&gt;
 };&lt;br /&gt;
 interface PropertyViewer {&lt;br /&gt;
    Property *getFirst();&lt;br /&gt;
    Property *getNext(Property &amp;amp;);&lt;br /&gt;
    Property *get(epicsInt32 id);&lt;br /&gt;
 }&lt;br /&gt;
 interface PropertyCatalogManager {&lt;br /&gt;
    epicsInt32 getId(EpicsString &amp;amp;);&lt;br /&gt;
    EpicsString *getName(epicsInt32 id);&lt;br /&gt;
    epicsInt32 createId(EpicsString &amp;amp;);&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
In the following, each method that uses the property name&lt;br /&gt;
will need a twin that uses the ID or Property *.&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* disconnected(Channel ch)&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* read(list&amp;lt;string&amp;gt; properties)&amp;lt;BR&amp;gt;Synchronous read of current values of properties.&lt;br /&gt;
* read(string property)&amp;lt;BR&amp;gt;Synchronous read of current value of one property.&lt;br /&gt;
* createSubscription(list&amp;lt;string&amp;gt; property, Event e, Filter f)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
** event is a set of any of the following:&lt;br /&gt;
*** exceeded absolute value deadband, exceeded percentage change, exceeded logarithmic change&lt;br /&gt;
*** minimum update period expired&lt;br /&gt;
*** alarm condition change&lt;br /&gt;
*** some hardware event (like blue beam)&lt;br /&gt;
** filters&lt;br /&gt;
*** override server-side idea of the value deadband, percentage change, log. change, minumum update rate, maximum update rate&lt;br /&gt;
*** specify value count&amp;lt;br&amp;gt;setting this to '1' turns the subscription into a single-value 'get'&lt;br /&gt;
* cancelSubscription(Event, Filter)&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read', 'write'.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Name_Server&amp;diff=2711</id>
		<title>V4 Name Server</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Name_Server&amp;diff=2711"/>
		<updated>2005-07-07T16:05:15Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Reduced to essentials&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Directory Server ==&lt;br /&gt;
&lt;br /&gt;
EPICS ChannelAccess currently uses a broadcast mechanism for resolving&lt;br /&gt;
channel names.&lt;br /&gt;
Advantages:&lt;br /&gt;
* Minimal if not zero initial setup.&lt;br /&gt;
* At least initially very fast, because a client can send multiple name requests in one network packet, all servers receive it simultaneously and can answer to known names.&lt;br /&gt;
&lt;br /&gt;
Disadvantages:&lt;br /&gt;
* Unclear how to extend this service to redundant servers, archived data, ...&lt;br /&gt;
* Currently, no wildcard searches.&lt;br /&gt;
* Currently, no statistics on available number of channels, types of records, ...&lt;br /&gt;
* Bigger installations run into problems with broadcast traffic:&lt;br /&gt;
** All devices see the requests, even though only one server has a given channel name.&lt;br /&gt;
** Continued searches for unresolved names to overcome UDP limitations.&lt;br /&gt;
&lt;br /&gt;
As Larry Hoff pointed out, we really want a ''Directory'' server for&lt;br /&gt;
EPICS, something beyond a name server. A directory will not only map PV&lt;br /&gt;
names to IP addresses of ChannelAccess servers, but also support&lt;br /&gt;
* wildcard searches, reports on number of channels per IOC etc.&lt;br /&gt;
* redundant servers&lt;br /&gt;
* info about reduntant IOCs with the same PV, archived data for a PV&lt;br /&gt;
* backup/restore locations for a PV&lt;br /&gt;
* PV meta information like engineer-in-charge, CA security rules, ...&lt;br /&gt;
&lt;br /&gt;
== Channels, Records, PVs, Properties ==&lt;br /&gt;
We currently have records with fields and channels with properties.&lt;br /&gt;
Each field of a record can become a channel name,&lt;br /&gt;
the field itself ends up in the 'value' property of the channel,&lt;br /&gt;
and the record decides how to fill the remaining properties.&lt;br /&gt;
&lt;br /&gt;
Will the directory list all record names or all possible channels?&lt;br /&gt;
I think only record names.&lt;br /&gt;
&lt;br /&gt;
Will it include the record type? Not enough information if we add user-defined fields.&lt;br /&gt;
Will it lists all fields of a record?&lt;br /&gt;
&lt;br /&gt;
I think for now that only listing record names is enough in order to allow&lt;br /&gt;
connections.&lt;br /&gt;
&lt;br /&gt;
The CA client would then have to query the CA server for all the available properties&lt;br /&gt;
of a PV, and note that properties might even change at runtime when we allow&lt;br /&gt;
online add &amp;amp; delete.&lt;br /&gt;
&lt;br /&gt;
Whenever a tool like a command-line 'caget' or the 'probe' GUI requires a PV name,&lt;br /&gt;
it needs to be decided what properties &amp;quot;fred&amp;quot; would access. In the past, it was equal&lt;br /&gt;
to &amp;quot;fred.VAL&amp;quot;. There is much more detail in the 'view' section of the&lt;br /&gt;
[[V4 DBD Statement Syntax]].&lt;br /&gt;
&lt;br /&gt;
== LDAP, Directory, RDB, Schema, Entries, dn, cn, dc, ... ==&lt;br /&gt;
&lt;br /&gt;
The ''Lightweight Directory Access Protocol'' (LDAP) is a network protocol.&lt;br /&gt;
It defines how to access data over the network, regardless of language&lt;br /&gt;
or underlying data storage. In contrast, RDBs like Oracle or MySQL define the&lt;br /&gt;
data storage and SQL dialect, while the network access to the data is then&lt;br /&gt;
added almost as an afterthought.&lt;br /&gt;
&lt;br /&gt;
LDAP is meant as a Directory for simple attribute-based information&lt;br /&gt;
for fast, high volume lookup. Simple search filters are supported:&lt;br /&gt;
Patterns, &amp;quot;and&amp;quot;, &amp;quot;or&amp;quot;. In contrast to RDBs, there is no support for&lt;br /&gt;
arbitrary relations between entries, there are no transactions nor rollback.&lt;br /&gt;
&lt;br /&gt;
There are open-source servers for LDAP, and client libraries for&lt;br /&gt;
C, Perl, Java and other languages. MacOSX, Linux, Windows 2000&lt;br /&gt;
already include LDAP clients. MS ActiveDirectory uses LDAP.&lt;br /&gt;
&lt;br /&gt;
LDAP requires the data to be organized into hierarchical ''Entries''.&lt;br /&gt;
Each Entry has a globally unique ''Distinguished Name'' (dn),&lt;br /&gt;
and various ''Attributes'', that is Type/Value pairs.&lt;br /&gt;
The ''Schema'' defines which attributes an entry must have and which ones&lt;br /&gt;
it might have. There are existing schemas for DNS-type data or personell&lt;br /&gt;
information, including types like ''Common Name'' (cn) or ''Domain Component'' (dc),&lt;br /&gt;
but one can add custom schemata. Values might include binary data, e.g. images.&lt;br /&gt;
&lt;br /&gt;
=== Mapping EPICS PV Information to LDAP ===&lt;br /&gt;
&lt;br /&gt;
Each LDAP entry must have a unique name (dn). EPICS PVs must be unique as well,&lt;br /&gt;
but only within the search domain currently defined by the EPICS_CA_ADDR_LIST.&lt;br /&gt;
It might make sense to synchronize LDAP servers across these boundaries,&lt;br /&gt;
in which case the LDAP dn will have to include more than just the PV name.&lt;br /&gt;
For example, one could have a PV &amp;quot;fred&amp;quot; in both the accelerator and office network&lt;br /&gt;
of the SNS, and enter them into an SNS-wide LDAP directory like this:&lt;br /&gt;
&lt;br /&gt;
 fred.linac.sns.epics:  cas=&amp;quot;linacioc1:5065&amp;quot;&lt;br /&gt;
 fred.office.sns.epics: cas=&amp;quot;testioc47.sns.ornl.gov:7987&amp;quot;, description=&amp;quot;Fred's test PV&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In this example, the directory includes the ChannelAccess server for both PVs,&lt;br /&gt;
and in one case also a description.&lt;br /&gt;
&lt;br /&gt;
While an EPICS-specific tool might present the data as shown above,&lt;br /&gt;
most generic LDAP tools and APIs use the ''LDAP Data Interchange Format'' (LDIF).&lt;br /&gt;
In LDIF, part of the above data could look like this:&lt;br /&gt;
&lt;br /&gt;
 dn: dc=epics&lt;br /&gt;
 objectclass: dcObject&lt;br /&gt;
 objectclass: organization&lt;br /&gt;
 o: Root of all EPICS PVs&lt;br /&gt;
 dc: epics&lt;br /&gt;
&lt;br /&gt;
 dn: dc=sns,dc=epics&lt;br /&gt;
 # Details omitted; similar to previous entry&lt;br /&gt;
&lt;br /&gt;
 dn: dc=linac,dc=sns,dc=epics&lt;br /&gt;
 objectclass: dcObject&lt;br /&gt;
 objectclass: organization&lt;br /&gt;
 o: Root of all EPICS PVs in the SNS Linac&lt;br /&gt;
 dc: linac&lt;br /&gt;
&lt;br /&gt;
 dn: cn=fred,dc=linac,dc=sns,dc=epics&lt;br /&gt;
 objectclass: ProcessVariable&lt;br /&gt;
 cn: fred&lt;br /&gt;
 cas: linacioc1:5065 &lt;br /&gt;
&lt;br /&gt;
Some points I had to learn:&lt;br /&gt;
* Each entry has an objectclass. The schema for &amp;quot;ProcessVariable&amp;quot; would for example require a &amp;quot;cn&amp;quot; attribute, with optional &amp;quot;cas&amp;quot;, &amp;quot;description&amp;quot; and maybe other attributes.&amp;lt;br&amp;gt;The schema would also determine if searches for PV names are case-sensitive or not.&lt;br /&gt;
* The hierarchical path &amp;quot;fred.linac.sns.epics&amp;quot; translates into a dn &amp;quot;cn=fred,dc=linac,dc=sns,dc=epics&amp;quot; which lists the relative dn of the entry itself and all path elements.&lt;br /&gt;
* Each path element must exists, so to bootstrap one has to create entries &amp;quot;dc=epics&amp;quot; and &amp;quot;dc=sns,dc=epics&amp;quot; etc., for which I used the pre-defined &amp;quot;organization&amp;quot; schema.&lt;br /&gt;
* To locate the PV 'fred', any of the following LDAP searches would work:&lt;br /&gt;
** Scope &amp;quot;base&amp;quot; with base &amp;quot;cn=fred,dc=linac,dc=sns,dc=epics&amp;quot;.&lt;br /&gt;
** Scope &amp;quot;one&amp;quot; with base &amp;quot;dc=linac,dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=fred)&amp;quot;.&lt;br /&gt;
** Scope &amp;quot;one&amp;quot; with base &amp;quot;dc=linac,dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=f*d)&amp;quot;,&amp;lt;br&amp;gt;though this might return more records which happen to match the pattern.&lt;br /&gt;
** Scope &amp;quot;sub&amp;quot; with base &amp;quot;dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=fred)&amp;quot;,&amp;lt;br&amp;gt;which would also return an entry with dn &amp;quot;cn=fred,dc=office,dc=sns,dc=epics&amp;quot;.&lt;br /&gt;
** There is a filter option &amp;quot;~=&amp;quot; for 'approximately equal'. Unclear what exactly defines approximate, but this might be very handy for detecing typos.&lt;br /&gt;
&lt;br /&gt;
=== OpenLDAP ===&lt;br /&gt;
&lt;br /&gt;
OpenLDAP  [http://www.openldap.org] is an open-source LDAP&lt;br /&gt;
client library for C and an LDAP server which can store the&lt;br /&gt;
actual data in /dev/null, a BerkeleyDB, SQL, Perl, a remote LDAP server.&lt;br /&gt;
&lt;br /&gt;
OpenLDAP includes authentication and encrypted (SSL) transport,&lt;br /&gt;
replication (single master -&amp;gt; multiple slaves),&lt;br /&gt;
and referrals from one server to another one which has more details.&lt;br /&gt;
&lt;br /&gt;
=== EPICS PV Tests ===&lt;br /&gt;
&lt;br /&gt;
Using OpenLDAP-stable-20050429 and perl-ldap-0.3202 on a 1.3 GHz PowerBook G4&lt;br /&gt;
with 780 MB RAM under MacOS X 10.3.8, I used a perl script to create&lt;br /&gt;
PV entries, search them, and remove them.&lt;br /&gt;
In addition, the 'ldapsearch' that comes with RedHat Enterprise AS 3 was&lt;br /&gt;
used to check remote accessibillity.&lt;br /&gt;
&lt;br /&gt;
When trying to list all PVs with an appropriate search pattern,&lt;br /&gt;
the default server configuration limits the response to 500 answers.&lt;br /&gt;
This was changed such that 'paged' searches which keep requesting data&lt;br /&gt;
in increments of e.g. 500 entries are allowed to continue until all data is retrieved.&lt;br /&gt;
From the command-line tool, that's done like this:&lt;br /&gt;
 ldapsearch -x -LLL -E pr=10/noprompt -b 'dc=epics' '(cn=testpv*)'  cas&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Based on the default OpenLDAP server config with an additional&lt;br /&gt;
index &amp;quot;cn pres,eq&amp;quot; on the common name of the PVs,&lt;br /&gt;
PVs with names 'testrec0000000001', 'testrec0000000002' etc.&lt;br /&gt;
were added, then looked up one-by-one via their exact name,&lt;br /&gt;
and finially deleted one by one.&lt;br /&gt;
&lt;br /&gt;
When using a C++ test client based on the OpenLDAP library,&lt;br /&gt;
the results for smaller numbers are:&lt;br /&gt;
 Add         10000 records : 22.76 secs&lt;br /&gt;
 Locate      10000 records : 6.49 secs&lt;br /&gt;
 Delete      10000 records : 22.20 secs&lt;br /&gt;
&lt;br /&gt;
In another test, a very rough estimated PV count of the SNS linac&lt;br /&gt;
was used:&lt;br /&gt;
 Add        200000 records : 675.89 secs (38.74 CPU)&lt;br /&gt;
 Locate     200000 records : 142.85 secs (27.91 CPU)&lt;br /&gt;
 Delete     200000 records : 807.28 secs (37.28 CPU)&lt;br /&gt;
&lt;br /&gt;
Those 200000 records use about 900MB of file space.&lt;br /&gt;
The results indicate that one can add 300 .. 440 records/sec,&lt;br /&gt;
locate 1400.. 1540 records/sec and delete 250..450 records/sec.&lt;br /&gt;
Without an index, the addition and deletion will be faster&lt;br /&gt;
at the expense of lookup times.&lt;br /&gt;
&lt;br /&gt;
A typical SNS LLRF IOCs has 2300 records.&lt;br /&gt;
On startup, sending those to the directory server would take around 7 seconds.&lt;br /&gt;
A typical SNS LLRF overview screen has about 400 PVs,&lt;br /&gt;
and current connection times vary from less than a second to&lt;br /&gt;
several tens of seconds, depending on how well the search requests&lt;br /&gt;
reach the IOCs.&lt;br /&gt;
Resolving their CA servers would take about 0.3 seconds with LDAP,&lt;br /&gt;
which would then be followed by the time required to actually connect&lt;br /&gt;
to those servers.&lt;br /&gt;
&lt;br /&gt;
In previous test using perl, the individual PV search actually used an LDIF&lt;br /&gt;
filter with exact match like 'testrec0000000001'. In addition, all PVs matching&lt;br /&gt;
a filter 'testrec*' were retrieved, and no index was used:&lt;br /&gt;
 Adding   1000 records:  4 wallclock secs ( 2.29 usr +  0.14 sys =  2.43 CPU)&lt;br /&gt;
 Locating 1000 records: 49 wallclock secs ( 4.10 usr +  0.21 sys =  4.31 CPU)&lt;br /&gt;
 Match all        1000:  1 wallclock secs ( 1.18 usr +  0.02 sys =  1.20 CPU)&lt;br /&gt;
 Delete   1000 records:  4 wallclock secs ( 1.35 usr +  0.14 sys =  1.49 CPU)&lt;br /&gt;
&lt;br /&gt;
When locating individual records via the complete LDIF base without a filter,&lt;br /&gt;
the search is much faster:&lt;br /&gt;
Locating 1000 records:  5 wallclock secs ( 3.20 usr +  0.18 sys =  3.38 CPU)&lt;br /&gt;
&lt;br /&gt;
When adding an &amp;quot;index cn pres,eq&amp;quot; to the server config, the results are the&lt;br /&gt;
same for both the filter and the base search case.&lt;br /&gt;
No difference observed when tripling the cachesize from the default of 1000.&lt;br /&gt;
The behavior scales linearly with the number of channels,&lt;br /&gt;
always a little over 200 additions or lookups per second:&lt;br /&gt;
 Adding   10000 records: 47 wallclock secs (21.72 usr +  1.28 sys = 23.00 CPU)&lt;br /&gt;
 Locating 10000 records: 44 wallclock secs (32.20 usr +  1.55 sys = 33.75 CPU)&lt;br /&gt;
 Match all        10000: 15 wallclock secs (12.15 usr +  0.64 sys = 12.79 CPU)&lt;br /&gt;
 Delete   10000 records: 38 wallclock secs (15.41 usr +  1.16 sys = 16.57 CPU)&lt;br /&gt;
&lt;br /&gt;
 Adding   50000 records: 243 wallclock secs (110.36 usr +  7.18 sys = 117.54 CPU)&lt;br /&gt;
 Locating 50000 records: 226 wallclock secs (164.97 usr +  7.67 sys = 172.64 CPU)&lt;br /&gt;
 Match all        50000: 80 wallclock secs (63.09 usr +  3.42 sys = 66.51 CPU)&lt;br /&gt;
 Delete   50000 records: 219 wallclock secs (82.11 usr +  6.61 sys = 88.72 CPU)&lt;br /&gt;
&lt;br /&gt;
 Adding  200000 records: 1293 wallclock secs (483.65 usr + 33.20 sys = 516.85 CPU)&lt;br /&gt;
&lt;br /&gt;
&amp;quot;index cn,dc pres,eq,approx,sub&amp;quot; takes longer to insert/delete&lt;br /&gt;
without improved search performance:&lt;br /&gt;
 Adding   10000 records: 104 wallclock secs (26.25 usr +  1.67 sys = 27.92 CPU)&lt;br /&gt;
 Locating 10000 records: 45 wallclock secs (32.86 usr +  1.55 sys = 34.41 CPU)&lt;br /&gt;
 Match all        10000: 15 wallclock secs (12.31 usr +  0.47 sys = 12.78 CPU)&lt;br /&gt;
 Delete   10000 records: 97 wallclock secs (17.89 usr +  1.34 sys = 19.23 CPU)&lt;br /&gt;
&lt;br /&gt;
The CPU load is a rough 50/50 split between the perl and slapd processes.&lt;br /&gt;
The perl ldap library is 100% perl, including the network handling and&lt;br /&gt;
quite some data conversions from arrays into hashes and back,&lt;br /&gt;
so a pure C client might be a little faster.&lt;br /&gt;
&lt;br /&gt;
Though an index slows down the insertion of PVs, it speeds up certain&lt;br /&gt;
retrieval methods, as probably desired for an EPICS directory server.&lt;br /&gt;
The search mechanism is based on round-trip requests.&lt;br /&gt;
Searching PVs one PV name at a time is slower than requesting all&lt;br /&gt;
PVs that match a pattern. Unfortunately, typical CA clients will&lt;br /&gt;
have to request data for specific PV names, not by pattern.&lt;br /&gt;
&lt;br /&gt;
==== JOH: For comparison purposes, here is the performance of CA's built-in name resolution. ====&lt;br /&gt;
&lt;br /&gt;
 Testing with 100000 channels named joh:bill&lt;br /&gt;
 channel connect test&lt;br /&gt;
 Elapsed Per Item =   0.00001683 sec,    59427.3 Items per sec, 31.4 Mbps&lt;br /&gt;
 Search tries per chan - mean=1.000930 std dev=0.030482 min=1.000000 max=2.000000&lt;br /&gt;
&lt;br /&gt;
== API ==&lt;br /&gt;
[[V4 CA Interfaces]] already has a lookup interface for a name service&lt;br /&gt;
that uses asynchronous callbacks and a property catalog to return the results.&lt;br /&gt;
&lt;br /&gt;
This is the interface I used for the test code. It is synchronous without configurable timeout (bad), but includes addition and removal of names (good), and the PVEntry lists the actual properties of an entry.&lt;br /&gt;
&lt;br /&gt;
  /// Client-Interface to the EPICS directory&lt;br /&gt;
  class Directory&lt;br /&gt;
  {&lt;br /&gt;
  public:&lt;br /&gt;
    /// Constuctor&lt;br /&gt;
    Directory();&lt;br /&gt;
&lt;br /&gt;
    /// Destructor (will disconnect)&lt;br /&gt;
    ~Directory();&lt;br /&gt;
&lt;br /&gt;
    /// Connect to directory server&lt;br /&gt;
    /// Better: URL ala &amp;quot;LDAP://cn=Manager,dc=epics:secret@localhost:389&amp;quot;&lt;br /&gt;
    /// which would allow other services without changing the API?&lt;br /&gt;
    bool connect(const char *host = &amp;quot;localhost:389&amp;quot;,&lt;br /&gt;
                 const char *user = &amp;quot;cn=Manager,dc=epics&amp;quot;,&lt;br /&gt;
                 const char *password = &amp;quot;secret&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    /// Disconnect from directory server&lt;br /&gt;
    void disconnect();&lt;br /&gt;
&lt;br /&gt;
    /// Directory entry for one PV&lt;br /&gt;
&lt;br /&gt;
    /// Unclear how to best deal with this with regard to real-time OS&lt;br /&gt;
    /// where we don't want to allocate too much mem at runtime.&lt;br /&gt;
    /// This API allows caller to reuse the same 'PVEntry' over and over.&lt;br /&gt;
    static const size_t SLEN = 100;&lt;br /&gt;
    static const size_t MAXCAS  = 3;&lt;br /&gt;
    static const char PATH_SEP = '/';&lt;br /&gt;
    class PVEntry&lt;br /&gt;
    {&lt;br /&gt;
    public:&lt;br /&gt;
        char pv[SLEN];&lt;br /&gt;
        char path[SLEN];&lt;br /&gt;
        char description[SLEN];&lt;br /&gt;
        char cas[MAXCAS][SLEN];&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    bool Directory::add_pventry(const PVEntry *pv);&lt;br /&gt;
&lt;br /&gt;
    /// Given pv-&amp;gt;pv, fills remaining PVEntry information from directory&lt;br /&gt;
    bool get_pventry(PVEntry *pv);&lt;br /&gt;
&lt;br /&gt;
    bool delete_pventry(const char *pv, const char *path);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
=== To be investigated ===&lt;br /&gt;
&lt;br /&gt;
* How to access it from vxWorks&lt;br /&gt;
* How does an LDAP server used for EPICS cooperate with existing LDAP servers for DNS, email etc. Should it be one and the same? Use special port numbers for 'EPICS' LDAP?&lt;br /&gt;
* API: What type of API would EPICS tools use? Whatever LDAP library they use? An 'EPICS wrapper' around LDAP? Gasper suggested to look at the Java JNDI API for inspiration.&lt;br /&gt;
* Common Database headaches:&lt;br /&gt;
** To simply get a  PV into LDAP, one has to check if this PV already exists and then either 'add' or 'modify'.&lt;br /&gt;
** What if a channel is no longer available? An IOC shutting down could remove PVs from the LDAP server, but an IOC that crashes won't.&lt;br /&gt;
* Replication: How to use it, how fast is it etc.&lt;br /&gt;
* Authentication and encryption: How hard is it to configure?&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Name_Server&amp;diff=359</id>
		<title>V4 Name Server</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Name_Server&amp;diff=359"/>
		<updated>2005-07-07T16:02:23Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Added test API&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Directory Server ==&lt;br /&gt;
&lt;br /&gt;
EPICS ChannelAccess currently uses a broadcast mechanism for resolving&lt;br /&gt;
channel names.&lt;br /&gt;
Advantages:&lt;br /&gt;
* Minimal if not zero initial setup.&lt;br /&gt;
* At least initially very fast, because a client can send multiple name requests in one network packet, all servers receive it simultaneously and can answer to known names.&lt;br /&gt;
&lt;br /&gt;
Disadvantages:&lt;br /&gt;
* Unclear how to extend this service to redundant servers, archived data, ...&lt;br /&gt;
* Currently, no wildcard searches.&lt;br /&gt;
* Currently, no statistics on available number of channels, types of records, ...&lt;br /&gt;
* Bigger installations run into problems with broadcast traffic:&lt;br /&gt;
** All devices see the requests, even though only one server has a given channel name.&lt;br /&gt;
** Continued searches for unresolved names to overcome UDP limitations.&lt;br /&gt;
&lt;br /&gt;
As Larry Hoff pointed out, we really want a ''Directory'' server for&lt;br /&gt;
EPICS, something beyond a name server. A directory will not only map PV&lt;br /&gt;
names to IP addresses of ChannelAccess servers, but also support&lt;br /&gt;
* wildcard searches, reports on number of channels per IOC etc.&lt;br /&gt;
* redundant servers&lt;br /&gt;
* info about reduntant IOCs with the same PV, archived data for a PV&lt;br /&gt;
* backup/restore locations for a PV&lt;br /&gt;
* PV meta information like engineer-in-charge, CA security rules, ...&lt;br /&gt;
&lt;br /&gt;
== Channels, Records, PVs, Properties ==&lt;br /&gt;
We currently have records with fields and channels with properties.&lt;br /&gt;
Each field of a record can become a channel name,&lt;br /&gt;
the field itself ends up in the 'value' property of the channel,&lt;br /&gt;
and the record decides how to fill the remaining properties.&lt;br /&gt;
&lt;br /&gt;
Will the directory list all record names or all possible channels?&lt;br /&gt;
I think only record names.&lt;br /&gt;
&lt;br /&gt;
Will it include the record type? Not enough information if we add user-defined fields.&lt;br /&gt;
Will it lists all fields of a record?&lt;br /&gt;
&lt;br /&gt;
I think for now that only listing record names is enough in order to allow&lt;br /&gt;
connections.&lt;br /&gt;
&lt;br /&gt;
The CA client would then have to query the CA server for all the available properties&lt;br /&gt;
of a PV, and note that properties might even change at runtime when we allow&lt;br /&gt;
online add &amp;amp; delete.&lt;br /&gt;
&lt;br /&gt;
Whenever a tool like a command-line 'caget' or the 'probe' GUI requires a PV name,&lt;br /&gt;
it needs to be decided what properties &amp;quot;fred&amp;quot; would access. In the past, it was equal&lt;br /&gt;
to &amp;quot;fred.VAL&amp;quot;. There is much more detail in the 'view' section of the&lt;br /&gt;
[[V4 DBD Statement Syntax]].&lt;br /&gt;
&lt;br /&gt;
== LDAP, Directory, RDB, Schema, Entries, dn, cn, dc, ... ==&lt;br /&gt;
&lt;br /&gt;
The ''Lightweight Directory Access Protocol'' (LDAP) is a network protocol.&lt;br /&gt;
It defines how to access data over the network, regardless of language&lt;br /&gt;
or underlying data storage. In contrast, RDBs like Oracle or MySQL define the&lt;br /&gt;
data storage and SQL dialect, while the network access to the data is then&lt;br /&gt;
added almost as an afterthought.&lt;br /&gt;
&lt;br /&gt;
LDAP is meant as a Directory for simple attribute-based information&lt;br /&gt;
for fast, high volume lookup. Simple search filters are supported:&lt;br /&gt;
Patterns, &amp;quot;and&amp;quot;, &amp;quot;or&amp;quot;. In contrast to RDBs, there is no support for&lt;br /&gt;
arbitrary relations between entries, there are no transactions nor rollback.&lt;br /&gt;
&lt;br /&gt;
There are open-source servers for LDAP, and client libraries for&lt;br /&gt;
C, Perl, Java and other languages. MacOSX, Linux, Windows 2000&lt;br /&gt;
already include LDAP clients. MS ActiveDirectory uses LDAP.&lt;br /&gt;
&lt;br /&gt;
LDAP requires the data to be organized into hierarchical ''Entries''.&lt;br /&gt;
Each Entry has a globally unique ''Distinguished Name'' (dn),&lt;br /&gt;
and various ''Attributes'', that is Type/Value pairs.&lt;br /&gt;
The ''Schema'' defines which attributes an entry must have and which ones&lt;br /&gt;
it might have. There are existing schemas for DNS-type data or personell&lt;br /&gt;
information, including types like ''Common Name'' (cn) or ''Domain Component'' (dc),&lt;br /&gt;
but one can add custom schemata. Values might include binary data, e.g. images.&lt;br /&gt;
&lt;br /&gt;
=== Mapping EPICS PV Information to LDAP ===&lt;br /&gt;
&lt;br /&gt;
Each LDAP entry must have a unique name (dn). EPICS PVs must be unique as well,&lt;br /&gt;
but only within the search domain currently defined by the EPICS_CA_ADDR_LIST.&lt;br /&gt;
It might make sense to synchronize LDAP servers across these boundaries,&lt;br /&gt;
in which case the LDAP dn will have to include more than just the PV name.&lt;br /&gt;
For example, one could have a PV &amp;quot;fred&amp;quot; in both the accelerator and office network&lt;br /&gt;
of the SNS, and enter them into an SNS-wide LDAP directory like this:&lt;br /&gt;
&lt;br /&gt;
 fred.linac.sns.epics:  cas=&amp;quot;linacioc1:5065&amp;quot;&lt;br /&gt;
 fred.office.sns.epics: cas=&amp;quot;testioc47.sns.ornl.gov:7987&amp;quot;, description=&amp;quot;Fred's test PV&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In this example, the directory includes the ChannelAccess server for both PVs,&lt;br /&gt;
and in one case also a description.&lt;br /&gt;
&lt;br /&gt;
While an EPICS-specific tool might present the data as shown above,&lt;br /&gt;
most generic LDAP tools and APIs use the ''LDAP Data Interchange Format'' (LDIF).&lt;br /&gt;
In LDIF, part of the above data could look like this:&lt;br /&gt;
&lt;br /&gt;
 dn: dc=epics&lt;br /&gt;
 objectclass: dcObject&lt;br /&gt;
 objectclass: organization&lt;br /&gt;
 o: Root of all EPICS PVs&lt;br /&gt;
 dc: epics&lt;br /&gt;
&lt;br /&gt;
 dn: dc=sns,dc=epics&lt;br /&gt;
 # Details omitted; similar to previous entry&lt;br /&gt;
&lt;br /&gt;
 dn: dc=linac,dc=sns,dc=epics&lt;br /&gt;
 objectclass: dcObject&lt;br /&gt;
 objectclass: organization&lt;br /&gt;
 o: Root of all EPICS PVs in the SNS Linac&lt;br /&gt;
 dc: linac&lt;br /&gt;
&lt;br /&gt;
 dn: cn=fred,dc=linac,dc=sns,dc=epics&lt;br /&gt;
 objectclass: ProcessVariable&lt;br /&gt;
 cn: fred&lt;br /&gt;
 cas: linacioc1:5065 &lt;br /&gt;
&lt;br /&gt;
Some points I had to learn:&lt;br /&gt;
* Each entry has an objectclass. The schema for &amp;quot;ProcessVariable&amp;quot; would for example require a &amp;quot;cn&amp;quot; attribute, with optional &amp;quot;cas&amp;quot;, &amp;quot;description&amp;quot; and maybe other attributes.&amp;lt;br&amp;gt;The schema would also determine if searches for PV names are case-sensitive or not.&lt;br /&gt;
* The hierarchical path &amp;quot;fred.linac.sns.epics&amp;quot; translates into a dn &amp;quot;cn=fred,dc=linac,dc=sns,dc=epics&amp;quot; which lists the relative dn of the entry itself and all path elements.&lt;br /&gt;
* Each path element must exists, so to bootstrap one has to create entries &amp;quot;dc=epics&amp;quot; and &amp;quot;dc=sns,dc=epics&amp;quot; etc., for which I used the pre-defined &amp;quot;organization&amp;quot; schema.&lt;br /&gt;
* To locate the PV 'fred', any of the following LDAP searches would work:&lt;br /&gt;
** Scope &amp;quot;base&amp;quot; with base &amp;quot;cn=fred,dc=linac,dc=sns,dc=epics&amp;quot;.&lt;br /&gt;
** Scope &amp;quot;one&amp;quot; with base &amp;quot;dc=linac,dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=fred)&amp;quot;.&lt;br /&gt;
** Scope &amp;quot;one&amp;quot; with base &amp;quot;dc=linac,dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=f*d)&amp;quot;,&amp;lt;br&amp;gt;though this might return more records which happen to match the pattern.&lt;br /&gt;
** Scope &amp;quot;sub&amp;quot; with base &amp;quot;dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=fred)&amp;quot;,&amp;lt;br&amp;gt;which would also return an entry with dn &amp;quot;cn=fred,dc=office,dc=sns,dc=epics&amp;quot;.&lt;br /&gt;
** There is a filter option &amp;quot;~=&amp;quot; for 'approximately equal'. Unclear what exactly defines approximate, but this might be very handy for detecing typos.&lt;br /&gt;
&lt;br /&gt;
=== OpenLDAP ===&lt;br /&gt;
&lt;br /&gt;
OpenLDAP  [http://www.openldap.org] is an open-source LDAP&lt;br /&gt;
client library for C and an LDAP server which can store the&lt;br /&gt;
actual data in /dev/null, a BerkeleyDB, SQL, Perl, a remote LDAP server.&lt;br /&gt;
&lt;br /&gt;
OpenLDAP includes authentication and encrypted (SSL) transport,&lt;br /&gt;
replication (single master -&amp;gt; multiple slaves),&lt;br /&gt;
and referrals from one server to another one which has more details.&lt;br /&gt;
&lt;br /&gt;
=== EPICS PV Tests ===&lt;br /&gt;
&lt;br /&gt;
Using OpenLDAP-stable-20050429 and perl-ldap-0.3202 on a 1.3 GHz PowerBook G4&lt;br /&gt;
with 780 MB RAM under MacOS X 10.3.8, I used a perl script to create&lt;br /&gt;
PV entries, search them, and remove them.&lt;br /&gt;
In addition, the 'ldapsearch' that comes with RedHat Enterprise AS 3 was&lt;br /&gt;
used to check remote accessibillity.&lt;br /&gt;
&lt;br /&gt;
When trying to list all PVs with an appropriate search pattern,&lt;br /&gt;
the default server configuration limits the response to 500 answers.&lt;br /&gt;
This was changed such that 'paged' searches which keep requesting data&lt;br /&gt;
in increments of e.g. 500 entries are allowed to continue until all data is retrieved.&lt;br /&gt;
From the command-line tool, that's done like this:&lt;br /&gt;
 ldapsearch -x -LLL -E pr=10/noprompt -b 'dc=epics' '(cn=testpv*)'  cas&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Based on the default OpenLDAP server config with an additional&lt;br /&gt;
index &amp;quot;cn pres,eq&amp;quot; on the common name of the PVs,&lt;br /&gt;
PVs with names 'testrec0000000001', 'testrec0000000002' etc.&lt;br /&gt;
were added, then looked up one-by-one via their exact name,&lt;br /&gt;
and finially deleted one by one.&lt;br /&gt;
&lt;br /&gt;
When using a C++ test client based on the OpenLDAP library,&lt;br /&gt;
the results for smaller numbers are:&lt;br /&gt;
 Add         10000 records : 22.76 secs&lt;br /&gt;
 Locate      10000 records : 6.49 secs&lt;br /&gt;
 Delete      10000 records : 22.20 secs&lt;br /&gt;
&lt;br /&gt;
In another test, a very rough estimated PV count of the SNS linac&lt;br /&gt;
was used:&lt;br /&gt;
 Add        200000 records : 675.89 secs (38.74 CPU)&lt;br /&gt;
 Locate     200000 records : 142.85 secs (27.91 CPU)&lt;br /&gt;
 Delete     200000 records : 807.28 secs (37.28 CPU)&lt;br /&gt;
&lt;br /&gt;
Those 200000 records use about 900MB of file space.&lt;br /&gt;
The results indicate that one can add 300 .. 440 records/sec,&lt;br /&gt;
locate 1400.. 1540 records/sec and delete 250..450 records/sec.&lt;br /&gt;
Without an index, the addition and deletion will be faster&lt;br /&gt;
at the expense of lookup times.&lt;br /&gt;
&lt;br /&gt;
A typical SNS LLRF IOCs has 2300 records.&lt;br /&gt;
On startup, sending those to the directory server would take around 7 seconds.&lt;br /&gt;
A typical SNS LLRF overview screen has about 400 PVs,&lt;br /&gt;
and current connection times vary from less than a second to&lt;br /&gt;
several tens of seconds, depending on how well the search requests&lt;br /&gt;
reach the IOCs.&lt;br /&gt;
Resolving their CA servers would take about 0.3 seconds with LDAP,&lt;br /&gt;
which would then be followed by the time required to actually connect&lt;br /&gt;
to those servers.&lt;br /&gt;
&lt;br /&gt;
In previous test using perl, the individual PV search actually used an LDIF&lt;br /&gt;
filter with exact match like 'testrec0000000001'. In addition, all PVs matching&lt;br /&gt;
a filter 'testrec*' were retrieved, and no index was used:&lt;br /&gt;
 Adding   1000 records:  4 wallclock secs ( 2.29 usr +  0.14 sys =  2.43 CPU)&lt;br /&gt;
 Locating 1000 records: 49 wallclock secs ( 4.10 usr +  0.21 sys =  4.31 CPU)&lt;br /&gt;
 Match all        1000:  1 wallclock secs ( 1.18 usr +  0.02 sys =  1.20 CPU)&lt;br /&gt;
 Delete   1000 records:  4 wallclock secs ( 1.35 usr +  0.14 sys =  1.49 CPU)&lt;br /&gt;
&lt;br /&gt;
When locating individual records via the complete LDIF base without a filter,&lt;br /&gt;
the search is much faster:&lt;br /&gt;
Locating 1000 records:  5 wallclock secs ( 3.20 usr +  0.18 sys =  3.38 CPU)&lt;br /&gt;
&lt;br /&gt;
When adding an &amp;quot;index cn pres,eq&amp;quot; to the server config, the results are the&lt;br /&gt;
same for both the filter and the base search case.&lt;br /&gt;
No difference observed when tripling the cachesize from the default of 1000.&lt;br /&gt;
The behavior scales linearly with the number of channels,&lt;br /&gt;
always a little over 200 additions or lookups per second:&lt;br /&gt;
 Adding   10000 records: 47 wallclock secs (21.72 usr +  1.28 sys = 23.00 CPU)&lt;br /&gt;
 Locating 10000 records: 44 wallclock secs (32.20 usr +  1.55 sys = 33.75 CPU)&lt;br /&gt;
 Match all        10000: 15 wallclock secs (12.15 usr +  0.64 sys = 12.79 CPU)&lt;br /&gt;
 Delete   10000 records: 38 wallclock secs (15.41 usr +  1.16 sys = 16.57 CPU)&lt;br /&gt;
&lt;br /&gt;
 Adding   50000 records: 243 wallclock secs (110.36 usr +  7.18 sys = 117.54 CPU)&lt;br /&gt;
 Locating 50000 records: 226 wallclock secs (164.97 usr +  7.67 sys = 172.64 CPU)&lt;br /&gt;
 Match all        50000: 80 wallclock secs (63.09 usr +  3.42 sys = 66.51 CPU)&lt;br /&gt;
 Delete   50000 records: 219 wallclock secs (82.11 usr +  6.61 sys = 88.72 CPU)&lt;br /&gt;
&lt;br /&gt;
 Adding  200000 records: 1293 wallclock secs (483.65 usr + 33.20 sys = 516.85 CPU)&lt;br /&gt;
&lt;br /&gt;
&amp;quot;index cn,dc pres,eq,approx,sub&amp;quot; takes longer to insert/delete&lt;br /&gt;
without improved search performance:&lt;br /&gt;
 Adding   10000 records: 104 wallclock secs (26.25 usr +  1.67 sys = 27.92 CPU)&lt;br /&gt;
 Locating 10000 records: 45 wallclock secs (32.86 usr +  1.55 sys = 34.41 CPU)&lt;br /&gt;
 Match all        10000: 15 wallclock secs (12.31 usr +  0.47 sys = 12.78 CPU)&lt;br /&gt;
 Delete   10000 records: 97 wallclock secs (17.89 usr +  1.34 sys = 19.23 CPU)&lt;br /&gt;
&lt;br /&gt;
The CPU load is a rough 50/50 split between the perl and slapd processes.&lt;br /&gt;
The perl ldap library is 100% perl, including the network handling and&lt;br /&gt;
quite some data conversions from arrays into hashes and back,&lt;br /&gt;
so a pure C client might be a little faster.&lt;br /&gt;
&lt;br /&gt;
Though an index slows down the insertion of PVs, it speeds up certain&lt;br /&gt;
retrieval methods, as probably desired for an EPICS directory server.&lt;br /&gt;
The search mechanism is based on round-trip requests.&lt;br /&gt;
Searching PVs one PV name at a time is slower than requesting all&lt;br /&gt;
PVs that match a pattern. Unfortunately, typical CA clients will&lt;br /&gt;
have to request data for specific PV names, not by pattern.&lt;br /&gt;
&lt;br /&gt;
==== JOH: For comparison purposes, here is the performance of CA's built-in name resolution. ====&lt;br /&gt;
&lt;br /&gt;
 Testing with 100000 channels named joh:bill&lt;br /&gt;
 channel connect test&lt;br /&gt;
 Elapsed Per Item =   0.00001683 sec,    59427.3 Items per sec, 31.4 Mbps&lt;br /&gt;
 Search tries per chan - mean=1.000930 std dev=0.030482 min=1.000000 max=2.000000&lt;br /&gt;
&lt;br /&gt;
== API ==&lt;br /&gt;
[[V4 CA Interfaces]] already has a lookup interface for a name service&lt;br /&gt;
that uses asynchronous callbacks and a property catalog to return the results.&lt;br /&gt;
&lt;br /&gt;
This is the interface I used for the test code. It is synchronous without configurable timeout (bad), but includes addition and removal of names (good), and the PVEntry lists the actual properties of an entry.&lt;br /&gt;
&lt;br /&gt;
  /// Client-Interface to the EPICS directory&lt;br /&gt;
  class Directory&lt;br /&gt;
  {&lt;br /&gt;
  public:&lt;br /&gt;
    /// Constuctor&lt;br /&gt;
    Directory();&lt;br /&gt;
&lt;br /&gt;
    /// Destructor (will disconnect)&lt;br /&gt;
    ~Directory();&lt;br /&gt;
&lt;br /&gt;
    /// Connect to directory server&lt;br /&gt;
    bool connect(const char *host = &amp;quot;localhost:389&amp;quot;,&lt;br /&gt;
                 const char *user = &amp;quot;cn=Manager,dc=epics&amp;quot;,&lt;br /&gt;
                 const char *password = &amp;quot;secret&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    /// Disconnect from directory server&lt;br /&gt;
    void disconnect();&lt;br /&gt;
&lt;br /&gt;
    /// Get some info string&lt;br /&gt;
    void api_info(char *buf, size_t len);&lt;br /&gt;
&lt;br /&gt;
    /// Useless API&lt;br /&gt;
    void dump_PVs();&lt;br /&gt;
&lt;br /&gt;
    /// Directory entry for one PV&lt;br /&gt;
&lt;br /&gt;
    /// Unclear how to best deal with this with regard to real-time OS&lt;br /&gt;
    /// where we don't want to allocate too much mem at runtime.&lt;br /&gt;
    /// This API allows caller to reuse the same 'PVEntry' over and over.&lt;br /&gt;
    static const size_t SLEN = 100;&lt;br /&gt;
    static const size_t MAXCAS  = 3;&lt;br /&gt;
    static const char PATH_SEP = '/';&lt;br /&gt;
    class PVEntry&lt;br /&gt;
    {&lt;br /&gt;
    public:&lt;br /&gt;
        char pv[SLEN];&lt;br /&gt;
        char path[SLEN];&lt;br /&gt;
        char description[SLEN];&lt;br /&gt;
        char cas[MAXCAS][SLEN];&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    bool Directory::add_pventry(const PVEntry *pv);&lt;br /&gt;
&lt;br /&gt;
    /// Given pv-&amp;gt;pv, fills remaining PVEntry information from directory&lt;br /&gt;
    bool get_pventry(PVEntry *pv);&lt;br /&gt;
&lt;br /&gt;
    bool delete_pventry(const char *pv, const char *path);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
=== To be investigated ===&lt;br /&gt;
&lt;br /&gt;
* How to access it from vxWorks&lt;br /&gt;
* How does an LDAP server used for EPICS cooperate with existing LDAP servers for DNS, email etc. Should it be one and the same? Use special port numbers for 'EPICS' LDAP?&lt;br /&gt;
* API: What type of API would EPICS tools use? Whatever LDAP library they use? An 'EPICS wrapper' around LDAP? Gasper suggested to look at the Java JNDI API for inspiration.&lt;br /&gt;
* Common Database headaches:&lt;br /&gt;
** To simply get a  PV into LDAP, one has to check if this PV already exists and then either 'add' or 'modify'.&lt;br /&gt;
** What if a channel is no longer available? An IOC shutting down could remove PVs from the LDAP server, but an IOC that crashes won't.&lt;br /&gt;
* Replication: How to use it, how fast is it etc.&lt;br /&gt;
* Authentication and encryption: How hard is it to configure?&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_User-defined_fields&amp;diff=432</id>
		<title>V4 User-defined fields</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_User-defined_fields&amp;diff=432"/>
		<updated>2005-07-06T13:52:28Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Link to record building blocks, Ben's comments&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Records, the building blocks of an EPICS run-time database,&lt;br /&gt;
are much more powerful than the fundamental commands&lt;br /&gt;
of programming languages like C, C++, LabVIEW or PLC&lt;br /&gt;
ladder logic: A record like the AI record provides scanning,&lt;br /&gt;
various conversions, alarms, smoothing and more.&lt;br /&gt;
&lt;br /&gt;
On the downside, very few record instances use the full functionality&lt;br /&gt;
of the record type. Extending their functionality is rather involved,&lt;br /&gt;
and very few custom record types get shared in the community.&lt;br /&gt;
&lt;br /&gt;
If it was easier to create, modify and extend records,&lt;br /&gt;
we could start with simpler record types and extend them&lt;br /&gt;
as needed.&lt;br /&gt;
&lt;br /&gt;
This is a suggestion for supporting user-defined fields in V4,&lt;br /&gt;
allowing the user to add fields to a record instance without&lt;br /&gt;
first having to create a new record type.&lt;br /&gt;
There is an alternate proposal,&lt;br /&gt;
[[V4 Design: Assembling Record Support]],&lt;br /&gt;
that would simplify the creation of new record types.&lt;br /&gt;
&lt;br /&gt;
== User-defined fields ==&lt;br /&gt;
&lt;br /&gt;
For example, I'd like to allow users to define &amp;quot;analog input&amp;quot; records&lt;br /&gt;
with the usual fields '''and''' allow them to add new fields to some&lt;br /&gt;
instance without having to create a new record type:&lt;br /&gt;
&lt;br /&gt;
 calc(&amp;quot;fred&amp;quot;)&lt;br /&gt;
 {&lt;br /&gt;
    field(SCAN, &amp;quot;1 second&amp;quot;)&lt;br /&gt;
    user_field(SMOO,  iocFloat64T, &amp;quot;0.5&amp;quot;, smoo_handler)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This record will have a 'SMOO' field of type double (float 64) that presumably implements&lt;br /&gt;
smoothing in the user-supplied smoo_handler.&lt;br /&gt;
&lt;br /&gt;
The idea is that one can add these user-defined fields on a per-instance basis&lt;br /&gt;
to pretty much any record type simply by editing the DB file and loading the&lt;br /&gt;
handler code, without having to recompile the rest of EPICS base.&lt;br /&gt;
&lt;br /&gt;
Example code for a ''handler'' and what the ''database'' code would have to do&lt;br /&gt;
follows.&lt;br /&gt;
&lt;br /&gt;
=== database.h ===&lt;br /&gt;
&lt;br /&gt;
 #include &amp;lt;iostream&amp;gt;&lt;br /&gt;
 #include &amp;lt;string&amp;gt;&lt;br /&gt;
 #include &amp;lt;list&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 using namespace std;&lt;br /&gt;
 &lt;br /&gt;
 enum iocType&lt;br /&gt;
 {&lt;br /&gt;
     iocUnknownT,iocBooleanT,iocOctetT,&lt;br /&gt;
     iocInt16T,iocUInt16T,iocInt32T,iocUInt32T,iocInt64T,iocUInt64T,&lt;br /&gt;
     iocFloat32T,iocFloat64T,&lt;br /&gt;
     iocStringT,iocMenuT,iocEnumT,iocLinkT,iocDeviceT,iocArrayT&lt;br /&gt;
 };&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // Support for user-defined fields in base&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // forwards&lt;br /&gt;
 class record;&lt;br /&gt;
 class user_field_handler;&lt;br /&gt;
 &lt;br /&gt;
 // Information for one user-defined field&lt;br /&gt;
 class user_field_data&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     string  name;&lt;br /&gt;
     iocType type;&lt;br /&gt;
     void    *data;&lt;br /&gt;
     string  parm;&lt;br /&gt;
     user_field_handler *handler;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // The 'handler' specified for a user field needs to implement&lt;br /&gt;
 // this interface:&lt;br /&gt;
 class user_field_handler&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     virtual void init(user_field_data *field, record *rec)     {}&lt;br /&gt;
     virtual void update(user_field_data *field, record *rec)   {}&lt;br /&gt;
     virtual void destroy(user_field_data *field, record *rec)  {}  &lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // Records need to support hooks similar to Java 'listeners'&lt;br /&gt;
 // so that user fields can hook into record processing.&lt;br /&gt;
 // Unclear at what levels hooks are needed.&lt;br /&gt;
 class got_data_hook&lt;br /&gt;
 {&lt;br /&gt;
 public:    virtual void run_got_data_hook()=0;&lt;br /&gt;
 };&lt;br /&gt;
 class post_monitor_hook&lt;br /&gt;
 {&lt;br /&gt;
 public:    virtual void run_post_monitor_hook()=0;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // Pieces of record code in order to show where the hooks&lt;br /&gt;
 // are handled inside of 'process()'&lt;br /&gt;
 class record&lt;br /&gt;
 {&lt;br /&gt;
 public:&lt;br /&gt;
     record(string name) : name(name) {}&lt;br /&gt;
     &lt;br /&gt;
     void add_user_field(string name, iocType type, string parm,&lt;br /&gt;
                         user_field_handler *handler)&lt;br /&gt;
     {&lt;br /&gt;
         user_field_data ufd;&lt;br /&gt;
         ufd.name = name;&lt;br /&gt;
         ufd.type = type;&lt;br /&gt;
         ufd.parm = parm;&lt;br /&gt;
         ufd.handler = handler;&lt;br /&gt;
         user_fields.push_back(ufd);&lt;br /&gt;
     }&lt;br /&gt;
     void add_got_data_hook(got_data_hook *hook)&lt;br /&gt;
     {&lt;br /&gt;
         got_data_hooks.push_back(hook);&lt;br /&gt;
     }&lt;br /&gt;
     // TODO: remove_got_data_hook();&lt;br /&gt;
     void add_post_monitor_hook(post_monitor_hook *hook)&lt;br /&gt;
     {&lt;br /&gt;
         post_monitor_hooks.push_back(hook);&lt;br /&gt;
     }&lt;br /&gt;
     // TODO: remove_post_monitor_hook();&lt;br /&gt;
 &lt;br /&gt;
     void iocInit();&lt;br /&gt;
 &lt;br /&gt;
     void process();&lt;br /&gt;
 private:&lt;br /&gt;
     string                     name;&lt;br /&gt;
     list&amp;lt;user_field_data&amp;gt;      user_fields;&lt;br /&gt;
     list&amp;lt;got_data_hook *&amp;gt;      got_data_hooks;&lt;br /&gt;
     list&amp;lt;post_monitor_hook *&amp;gt;  post_monitor_hooks;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== database.cpp ===&lt;br /&gt;
&lt;br /&gt;
 #include &amp;quot;database.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 void *get_iocType_mem(iocType type)&lt;br /&gt;
 {&lt;br /&gt;
     switch (type)&lt;br /&gt;
     {&lt;br /&gt;
         case iocFloat32T: return new float;&lt;br /&gt;
         case iocFloat64T: return new double;&lt;br /&gt;
         case iocStringT:  return new string;&lt;br /&gt;
     }&lt;br /&gt;
     return 0;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 void record::iocInit()&lt;br /&gt;
 {&lt;br /&gt;
     cout &amp;lt;&amp;lt; &amp;quot;record::iocInit(&amp;quot; &amp;lt;&amp;lt; name &amp;lt;&amp;lt; &amp;quot;)\n&amp;quot;;&lt;br /&gt;
     list&amp;lt;user_field_data&amp;gt;::iterator ufi;&lt;br /&gt;
     for (ufi = user_fields.begin();  ufi != user_fields.end();  ++ufi)&lt;br /&gt;
     {&lt;br /&gt;
         if ((ufi-&amp;gt;data = get_iocType_mem(ufi-&amp;gt;type)) != 0)&lt;br /&gt;
             ufi-&amp;gt;handler-&amp;gt;init(&amp;amp;(*ufi), this);&lt;br /&gt;
         else&lt;br /&gt;
             ufi-&amp;gt;type = iocUnknownT;&lt;br /&gt;
     }   &lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 void record::process()&lt;br /&gt;
 {&lt;br /&gt;
     cout &amp;lt;&amp;lt; &amp;quot;record::process(&amp;quot; &amp;lt;&amp;lt; name &amp;lt;&amp;lt; &amp;quot;)\n&amp;quot;;&lt;br /&gt;
 #ifdef TODO&lt;br /&gt;
     status=readValue(pai); /* read the new value */&lt;br /&gt;
     if ( !pact &amp;amp;&amp;amp; pai-&amp;gt;pact ) return(0);&lt;br /&gt;
     pai-&amp;gt;pact = TRUE;&lt;br /&gt;
     &lt;br /&gt;
     recGblGetTimeStamp(pai);&lt;br /&gt;
     if (status==0) convert(pai);&lt;br /&gt;
     else if (status==2) status=0;&lt;br /&gt;
 #endif&lt;br /&gt;
     list&amp;lt;got_data_hook *&amp;gt;::iterator gdhi;&lt;br /&gt;
     for (gdhi = got_data_hooks.begin(); gdhi != got_data_hooks.end(); ++gdhi)&lt;br /&gt;
         (*gdhi)-&amp;gt;run_got_data_hook();&lt;br /&gt;
 #ifdef TODO&lt;br /&gt;
     checkAlarms(pai);&lt;br /&gt;
     monitor(pai);&lt;br /&gt;
 #endif&lt;br /&gt;
     list&amp;lt;post_monitor_hook *&amp;gt;::iterator pmhi;&lt;br /&gt;
     for (pmhi=post_monitor_hooks.begin(); pmhi!=post_monitor_hooks.end(); ++pmhi)&lt;br /&gt;
         (*pmhi)-&amp;gt;run_post_monitor_hook();&lt;br /&gt;
 #ifdef TODO&lt;br /&gt;
     pai-&amp;gt;pact=FALSE;&lt;br /&gt;
 #endif&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // TODO:&lt;br /&gt;
 // Record will need to check all it's user_fields[].name&lt;br /&gt;
 // in case CA tries to get/put.&lt;br /&gt;
&lt;br /&gt;
=== example.cpp ===&lt;br /&gt;
&lt;br /&gt;
 /* Goals:&lt;br /&gt;
 - Avg or other statistical calc:&lt;br /&gt;
   deposits calculated data into new fields,&lt;br /&gt;
   doesn't affect the rest of the record.&lt;br /&gt;
 - Circ Buffer (archive record):&lt;br /&gt;
   Similar, does something with the current VAL&lt;br /&gt;
   outside of the record.&lt;br /&gt;
 - Put Logging: Simlar.&lt;br /&gt;
 - SMOO: Can it modify the VAL field?&lt;br /&gt;
 - BPM combiner of 4 AIs: Unclear.&lt;br /&gt;
 - conversions (new breakpt. table): unclear.&lt;br /&gt;
 - FLNK (w/ delays?): Should happen _after_ all else is done.&lt;br /&gt;
 &lt;br /&gt;
 The following implements handlers for some of the above.&lt;br /&gt;
 &lt;br /&gt;
 */&lt;br /&gt;
 #include &amp;quot;database.h&amp;quot;&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // MAX (easier to calc. then e.g. running average&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 class max_handler : public user_field_handler, got_data_hook&lt;br /&gt;
 {&lt;br /&gt;
     void init(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         max = (double *)field-&amp;gt;data;&lt;br /&gt;
         // TODO: val = record-&amp;gt;get_field_addr(&amp;quot;VAL&amp;quot;)&lt;br /&gt;
         rec-&amp;gt;add_got_data_hook(this);&lt;br /&gt;
     }&lt;br /&gt;
     void run_got_data_hook()&lt;br /&gt;
     {&lt;br /&gt;
         // if (*val &amp;gt; *max)   *max = *val;&lt;br /&gt;
         cout &amp;lt;&amp;lt; &amp;quot;Max is &amp;quot; &amp;lt;&amp;lt; *max &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     double *max;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // SMOO&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 class smoo_handler : public user_field_handler, got_data_hook&lt;br /&gt;
 {&lt;br /&gt;
     void init(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         smoo = (double *)field-&amp;gt;data;&lt;br /&gt;
         *smoo = atof(field-&amp;gt;parm.c_str());&lt;br /&gt;
         // TODO: val = record-&amp;gt;get_field_addr(&amp;quot;VAL&amp;quot;)&lt;br /&gt;
         rec-&amp;gt;add_got_data_hook(this);&lt;br /&gt;
     }&lt;br /&gt;
     void run_got_data_hook()&lt;br /&gt;
     {&lt;br /&gt;
         // *val = smoo*(*val) + (1-smoo)*prev_value;&lt;br /&gt;
         // prev_value = *val;&lt;br /&gt;
         cout &amp;lt;&amp;lt; &amp;quot;Smoothing by &amp;quot; &amp;lt;&amp;lt; *smoo &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     double *smoo;&lt;br /&gt;
     double prev_value;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // FLNK&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 class flnk_handler : public user_field_handler, post_monitor_hook&lt;br /&gt;
 {&lt;br /&gt;
     void init(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         link_text = (string *)field-&amp;gt;data;&lt;br /&gt;
         *link_text = field-&amp;gt;parm;&lt;br /&gt;
         update(field, rec);&lt;br /&gt;
         rec-&amp;gt;add_post_monitor_hook(this);&lt;br /&gt;
     }&lt;br /&gt;
     void update(user_field_data *field, record *rec)&lt;br /&gt;
     {&lt;br /&gt;
         // TODO: ink_info = parse_link_info_from_string(*link_text);&lt;br /&gt;
         link_info = *link_text;&lt;br /&gt;
     }&lt;br /&gt;
     void run_post_monitor_hook()&lt;br /&gt;
     {&lt;br /&gt;
         cout &amp;lt;&amp;lt; &amp;quot;FLNKing to &amp;quot; &amp;lt;&amp;lt; link_info &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
         // TODO: dbProcessLink(this-&amp;gt;link_info);&lt;br /&gt;
         // Or with additional delay parm:&lt;br /&gt;
         // wdStart(delay, dbProcessLink, this-&amp;gt;link_info);  ?&lt;br /&gt;
     }&lt;br /&gt;
 private:&lt;br /&gt;
     string *link_text;&lt;br /&gt;
     string link_info;&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 // Example database snippet and how it's used&lt;br /&gt;
 // ------------------------------------------------------------------&lt;br /&gt;
 /*&lt;br /&gt;
 ai(&amp;quot;fred&amp;quot;)&lt;br /&gt;
 {&lt;br /&gt;
   #          NAME, TYPE,         PARM,      HANDLER&lt;br /&gt;
   user_field(smoo,  iocFloat64T, &amp;quot;0.5&amp;quot;,     smoo_handler)&lt;br /&gt;
   user_field(max,   iocFloat64T, &amp;quot;&amp;quot;,        max_handler)&lt;br /&gt;
   user_field(flnk1, iocStringT,  &amp;quot;recordA&amp;quot;, flnk_handler)&lt;br /&gt;
   user_field(flnk2, iocStringT,  &amp;quot;recordB&amp;quot;, flnk_handler)&lt;br /&gt;
   user_field(flnk3, iocStringT,  &amp;quot;recordC&amp;quot;, flnk_handler)&lt;br /&gt;
   # In these examples, the PARM is the initial value of the field.&lt;br /&gt;
   # But it could be any string that's used to configure the handler:&lt;br /&gt;
   # user_field(syslog, iocStringT, &amp;quot;server=124.0.0.5:9876,interval=1&amp;quot;, syslog_handler)&lt;br /&gt;
 }      &lt;br /&gt;
 */&lt;br /&gt;
 int main()&lt;br /&gt;
 {&lt;br /&gt;
     // This is sort of what would need to happen inside dbLoadDatabase().&lt;br /&gt;
     // The tricky part is the &amp;quot;new xxx_handler&amp;quot;:&lt;br /&gt;
     // Each handler will have to use the registry to register a factory&lt;br /&gt;
     // method so that dbLoadDatabase can invoke &amp;quot;new xxx_handler&amp;quot; without&lt;br /&gt;
     // recompilation.&lt;br /&gt;
     record fred(&amp;quot;fred&amp;quot;);&lt;br /&gt;
     fred.add_user_field(&amp;quot;smoo&amp;quot;, iocFloat64T, &amp;quot;0.5&amp;quot;,     new smoo_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;max&amp;quot;,  iocFloat64T, &amp;quot;&amp;quot;,        new max_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;flnk1&amp;quot;, iocStringT, &amp;quot;recordA&amp;quot;, new flnk_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;flnk2&amp;quot;, iocStringT, &amp;quot;recordB&amp;quot;, new flnk_handler());&lt;br /&gt;
     fred.add_user_field(&amp;quot;flnk3&amp;quot;, iocStringT, &amp;quot;recordC&amp;quot;, new flnk_handler());&lt;br /&gt;
     //&lt;br /&gt;
     fred.iocInit();&lt;br /&gt;
     //&lt;br /&gt;
     fred.process();&lt;br /&gt;
     cout &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
     fred.process();&lt;br /&gt;
     &lt;br /&gt;
     return 0;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 /*&lt;br /&gt;
 When run, this basically processes the record and the&lt;br /&gt;
 code for the user-defined fields is invoked as intended:&lt;br /&gt;
 &lt;br /&gt;
 record::iocInit(fred)&lt;br /&gt;
 record::process(fred)&lt;br /&gt;
 Smoothing by 0.5&lt;br /&gt;
 Max is 0&lt;br /&gt;
 FLNKing to recordA&lt;br /&gt;
 FLNKing to recordB&lt;br /&gt;
 FLNKing to recordC&lt;br /&gt;
 &lt;br /&gt;
 record::process(fred)&lt;br /&gt;
 Smoothing by 0.5&lt;br /&gt;
 Max is 0&lt;br /&gt;
 FLNKing to recordA&lt;br /&gt;
 FLNKing to recordB&lt;br /&gt;
 FLNKing to recordC&lt;br /&gt;
 &lt;br /&gt;
 UNCLEAR:&lt;br /&gt;
 Is string parm enough, or are structs requires?&lt;br /&gt;
 &lt;br /&gt;
 I believe that all the goals 1-7 can be handled&lt;br /&gt;
 by providing the appropriate hooks.&lt;br /&gt;
 Question: How many hooks will that be?&lt;br /&gt;
   &lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Comments ==&lt;br /&gt;
The example code for user_field_handler::init() &lt;br /&gt;
assumes that the record has&lt;br /&gt;
a routine  get_field_addr(string name) that provides a raw pointer&lt;br /&gt;
to each field of a record. Would have to use dataAccess or whatever the&lt;br /&gt;
runtime database will ultimately provide.&lt;br /&gt;
&lt;br /&gt;
Some pro and cons when comparing this type of user-defined field to writing a custom record:&lt;br /&gt;
&lt;br /&gt;
=== Pro ===&lt;br /&gt;
* One can add fields per record instance as needed. No need to create a possibly confusing plethora of specialized record types:  BasicAI, MaxAI, AverageAI, AverageAndMaxAI, BasicAO, MaxAO, ...&lt;br /&gt;
* Under vxWorks, one can do so without recompilation:&lt;br /&gt;
   ld &amp;lt;smoo_handlerLib.o&lt;br /&gt;
   dbLoadDataBase(&amp;quot;..../records_that_use_smoo_handler&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
=== Contra ===&lt;br /&gt;
* The actions inside a user-field handler are limited to quick, synchronous actions.&lt;br /&gt;
* In order to handle the goals mentioned in example.cpp, only two hooks into the record's process() routine are needed. Unclear if this is sufficient in the long run.&lt;br /&gt;
* While easy to use, the code requires more runtime checks, more type casts, more boilerplate code. For example, the smoo_handler should react accordingly if the record does not have a numeric value field. In a custom record type, compile-time checks would reduce the code size and increase maintainabililty.&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Name_Server&amp;diff=353</id>
		<title>V4 Name Server</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Name_Server&amp;diff=353"/>
		<updated>2005-06-29T16:17:48Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: views&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Directory Server ==&lt;br /&gt;
&lt;br /&gt;
EPICS ChannelAccess currently uses a broadcast mechanism for resolving&lt;br /&gt;
channel names.&lt;br /&gt;
Advantages:&lt;br /&gt;
* Minimal if not zero initial setup.&lt;br /&gt;
* At least initially very fast, because a client can send multiple name requests in one network packet, all servers receive it simultaneously and can answer to known names.&lt;br /&gt;
&lt;br /&gt;
Disadvantages:&lt;br /&gt;
* Unclear how to extend this service to redundant servers, archived data, ...&lt;br /&gt;
* Currently, no wildcard searches.&lt;br /&gt;
* Currently, no statistics on available number of channels, types of records, ...&lt;br /&gt;
* Bigger installations run into problems with broadcast traffic:&lt;br /&gt;
** All devices see the requests, even though only one server has a given channel name.&lt;br /&gt;
** Continued searches for unresolved names to overcome UDP limitations.&lt;br /&gt;
&lt;br /&gt;
As Larry Hoff pointed out, we really want a ''Directory'' server for&lt;br /&gt;
EPICS, something beyond a name server. A directory will not only map PV&lt;br /&gt;
names to IP addresses of ChannelAccess servers, but also support&lt;br /&gt;
* wildcard searches, reports on number of channels per IOC etc.&lt;br /&gt;
* redundant servers&lt;br /&gt;
* info about reduntant IOCs with the same PV, archived data for a PV&lt;br /&gt;
* backup/restore locations for a PV&lt;br /&gt;
* PV meta information like engineer-in-charge, CA security rules, ...&lt;br /&gt;
&lt;br /&gt;
== Channels, Records, PVs, Properties ==&lt;br /&gt;
We currently have records with fields and channels with properties.&lt;br /&gt;
Each field of a record can become a channel name,&lt;br /&gt;
the field itself ends up in the 'value' property of the channel,&lt;br /&gt;
and the record decides how to fill the remaining properties.&lt;br /&gt;
&lt;br /&gt;
Will the directory list all record names or all possible channels?&lt;br /&gt;
I think only record names.&lt;br /&gt;
&lt;br /&gt;
Will it include the record type? Not enough information if we add user-defined fields.&lt;br /&gt;
Will it lists all fields of a record?&lt;br /&gt;
&lt;br /&gt;
I think for now that only listing record names is enough in order to allow&lt;br /&gt;
connections.&lt;br /&gt;
&lt;br /&gt;
The CA client would then have to query the CA server for all the available properties&lt;br /&gt;
of a PV, and note that properties might even change at runtime when we allow&lt;br /&gt;
online add &amp;amp; delete.&lt;br /&gt;
&lt;br /&gt;
Whenever a tool like a command-line 'caget' or the 'probe' GUI requires a PV name,&lt;br /&gt;
it needs to be decided what properties &amp;quot;fred&amp;quot; would access. In the past, it was equal&lt;br /&gt;
to &amp;quot;fred.VAL&amp;quot;. There is much more detail in the 'view' section of the&lt;br /&gt;
[[V4 DBD Statement Syntax]].&lt;br /&gt;
&lt;br /&gt;
== LDAP, Directory, RDB, Schema, Entries, dn, cn, dc, ... ==&lt;br /&gt;
&lt;br /&gt;
The ''Lightweight Directory Access Protocol'' (LDAP) is a network protocol.&lt;br /&gt;
It defines how to access data over the network, regardless of language&lt;br /&gt;
or underlying data storage. In contrast, RDBs like Oracle or MySQL define the&lt;br /&gt;
data storage and SQL dialect, while the network access to the data is then&lt;br /&gt;
added almost as an afterthought.&lt;br /&gt;
&lt;br /&gt;
LDAP is meant as a Directory for simple attribute-based information&lt;br /&gt;
for fast, high volume lookup. Simple search filters are supported:&lt;br /&gt;
Patterns, &amp;quot;and&amp;quot;, &amp;quot;or&amp;quot;. In contrast to RDBs, there is no support for&lt;br /&gt;
arbitrary relations between entries, there are no transactions nor rollback.&lt;br /&gt;
&lt;br /&gt;
There are open-source servers for LDAP, and client libraries for&lt;br /&gt;
C, Perl, Java and other languages. MacOSX, Linux, Windows 2000&lt;br /&gt;
already include LDAP clients. MS ActiveDirectory uses LDAP.&lt;br /&gt;
&lt;br /&gt;
LDAP requires the data to be organized into hierarchical ''Entries''.&lt;br /&gt;
Each Entry has a globally unique ''Distinguished Name'' (dn),&lt;br /&gt;
and various ''Attributes'', that is Type/Value pairs.&lt;br /&gt;
The ''Schema'' defines which attributes an entry must have and which ones&lt;br /&gt;
it might have. There are existing schemas for DNS-type data or personell&lt;br /&gt;
information, including types like ''Common Name'' (cn) or ''Domain Component'' (dc),&lt;br /&gt;
but one can add custom schemata. Values might include binary data, e.g. images.&lt;br /&gt;
&lt;br /&gt;
=== Mapping EPICS PV Information to LDAP ===&lt;br /&gt;
&lt;br /&gt;
Each LDAP entry must have a unique name (dn). EPICS PVs must be unique as well,&lt;br /&gt;
but only within the search domain currently defined by the EPICS_CA_ADDR_LIST.&lt;br /&gt;
It might make sense to synchronize LDAP servers across these boundaries,&lt;br /&gt;
in which case the LDAP dn will have to include more than just the PV name.&lt;br /&gt;
For example, one could have a PV &amp;quot;fred&amp;quot; in both the accelerator and office network&lt;br /&gt;
of the SNS, and enter them into an SNS-wide LDAP directory like this:&lt;br /&gt;
&lt;br /&gt;
 fred.linac.sns.epics:  cas=&amp;quot;linacioc1:5065&amp;quot;&lt;br /&gt;
 fred.office.sns.epics: cas=&amp;quot;testioc47.sns.ornl.gov:7987&amp;quot;, description=&amp;quot;Fred's test PV&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In this example, the directory includes the ChannelAccess server for both PVs,&lt;br /&gt;
and in one case also a description.&lt;br /&gt;
&lt;br /&gt;
While an EPICS-specific tool might present the data as shown above,&lt;br /&gt;
most generic LDAP tools and APIs use the ''LDAP Data Interchange Format'' (LDIF).&lt;br /&gt;
In LDIF, part of the above data could look like this:&lt;br /&gt;
&lt;br /&gt;
 dn: dc=epics&lt;br /&gt;
 objectclass: dcObject&lt;br /&gt;
 objectclass: organization&lt;br /&gt;
 o: Root of all EPICS PVs&lt;br /&gt;
 dc: epics&lt;br /&gt;
&lt;br /&gt;
 dn: dc=sns,dc=epics&lt;br /&gt;
 # Details omitted; similar to previous entry&lt;br /&gt;
&lt;br /&gt;
 dn: dc=linac,dc=sns,dc=epics&lt;br /&gt;
 objectclass: dcObject&lt;br /&gt;
 objectclass: organization&lt;br /&gt;
 o: Root of all EPICS PVs in the SNS Linac&lt;br /&gt;
 dc: linac&lt;br /&gt;
&lt;br /&gt;
 dn: cn=fred,dc=linac,dc=sns,dc=epics&lt;br /&gt;
 objectclass: ProcessVariable&lt;br /&gt;
 cn: fred&lt;br /&gt;
 cas: linacioc1:5065 &lt;br /&gt;
&lt;br /&gt;
Some points I had to learn:&lt;br /&gt;
* Each entry has an objectclass. The schema for &amp;quot;ProcessVariable&amp;quot; would for example require a &amp;quot;cn&amp;quot; attribute, with optional &amp;quot;cas&amp;quot;, &amp;quot;description&amp;quot; and maybe other attributes.&amp;lt;br&amp;gt;The schema would also determine if searches for PV names are case-sensitive or not.&lt;br /&gt;
* The hierarchical path &amp;quot;fred.linac.sns.epics&amp;quot; translates into a dn &amp;quot;cn=fred,dc=linac,dc=sns,dc=epics&amp;quot; which lists the relative dn of the entry itself and all path elements.&lt;br /&gt;
* Each path element must exists, so to bootstrap one has to create entries &amp;quot;dc=epics&amp;quot; and &amp;quot;dc=sns,dc=epics&amp;quot; etc., for which I used the pre-defined &amp;quot;organization&amp;quot; schema.&lt;br /&gt;
* To locate the PV 'fred', any of the following LDAP searches would work:&lt;br /&gt;
** Scope &amp;quot;base&amp;quot; with base &amp;quot;cn=fred,dc=linac,dc=sns,dc=epics&amp;quot;.&lt;br /&gt;
** Scope &amp;quot;one&amp;quot; with base &amp;quot;dc=linac,dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=fred)&amp;quot;.&lt;br /&gt;
** Scope &amp;quot;one&amp;quot; with base &amp;quot;dc=linac,dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=f*d)&amp;quot;,&amp;lt;br&amp;gt;though this might return more records which happen to match the pattern.&lt;br /&gt;
** Scope &amp;quot;sub&amp;quot; with base &amp;quot;dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=fred)&amp;quot;,&amp;lt;br&amp;gt;which would also return an entry with dn &amp;quot;cn=fred,dc=office,dc=sns,dc=epics&amp;quot;.&lt;br /&gt;
** There is a filter option &amp;quot;~=&amp;quot; for 'approximately equal'. Unclear what exactly defines approximate, but this might be very handy for detecing typos.&lt;br /&gt;
&lt;br /&gt;
=== OpenLDAP ===&lt;br /&gt;
&lt;br /&gt;
OpenLDAP  [http://www.openldap.org] is an open-source LDAP&lt;br /&gt;
client library for C and an LDAP server which can store the&lt;br /&gt;
actual data in /dev/null, a BerkeleyDB, SQL, Perl, a remote LDAP server.&lt;br /&gt;
&lt;br /&gt;
OpenLDAP includes authentication and encrypted (SSL) transport,&lt;br /&gt;
replication (single master -&amp;gt; multiple slaves),&lt;br /&gt;
and referrals from one server to another one which has more details.&lt;br /&gt;
&lt;br /&gt;
=== EPICS PV Tests ===&lt;br /&gt;
&lt;br /&gt;
Using OpenLDAP-stable-20050429 and perl-ldap-0.3202 on a 1.3 GHz PowerBook G4&lt;br /&gt;
with 780 MB RAM under MacOS X 10.3.8, I used a perl script to create&lt;br /&gt;
PV entries, search them, and remove them.&lt;br /&gt;
In addition, the 'ldapsearch' that comes with RedHat Enterprise AS 3 was&lt;br /&gt;
used to check remote accessibillity.&lt;br /&gt;
&lt;br /&gt;
When trying to list all PVs with an appropriate search pattern,&lt;br /&gt;
the default server configuration limits the response to 500 answers.&lt;br /&gt;
This was changed such that 'paged' searches which keep requesting data&lt;br /&gt;
in increments of e.g. 500 entries are allowed to continue until all data is retrieved.&lt;br /&gt;
From the command-line tool, that's done like this:&lt;br /&gt;
 ldapsearch -x -LLL -E pr=10/noprompt -b 'dc=epics' '(cn=testpv*)'  cas&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Based on the default OpenLDAP server config with an additional&lt;br /&gt;
index &amp;quot;cn pres,eq&amp;quot; on the common name of the PVs,&lt;br /&gt;
PVs with names 'testrec0000000001', 'testrec0000000002' etc.&lt;br /&gt;
were added, then looked up one-by-one via their exact name,&lt;br /&gt;
and finially deleted one by one.&lt;br /&gt;
&lt;br /&gt;
When using a C++ test client based on the OpenLDAP library,&lt;br /&gt;
the results for smaller numbers are:&lt;br /&gt;
 Add         10000 records : 22.76 secs&lt;br /&gt;
 Locate      10000 records : 6.49 secs&lt;br /&gt;
 Delete      10000 records : 22.20 secs&lt;br /&gt;
&lt;br /&gt;
In another test, a very rough estimated PV count of the SNS linac&lt;br /&gt;
was used:&lt;br /&gt;
 Add        200000 records : 675.89 secs (38.74 CPU)&lt;br /&gt;
 Locate     200000 records : 142.85 secs (27.91 CPU)&lt;br /&gt;
 Delete     200000 records : 807.28 secs (37.28 CPU)&lt;br /&gt;
&lt;br /&gt;
Those 200000 records use about 900MB of file space.&lt;br /&gt;
The results indicate that one can add 300 .. 440 records/sec,&lt;br /&gt;
locate 1400.. 1540 records/sec and delete 250..450 records/sec.&lt;br /&gt;
Without an index, the addition and deletion will be faster&lt;br /&gt;
at the expense of lookup times.&lt;br /&gt;
&lt;br /&gt;
A typical SNS LLRF IOCs has 2300 records.&lt;br /&gt;
On startup, sending those to the directory server would take around 7 seconds.&lt;br /&gt;
A typical SNS LLRF overview screen has about 400 PVs,&lt;br /&gt;
and current connection times vary from less than a second to&lt;br /&gt;
several tens of seconds, depending on how well the search requests&lt;br /&gt;
reach the IOCs.&lt;br /&gt;
Resolving their CA servers would take about 0.3 seconds with LDAP,&lt;br /&gt;
which would then be followed by the time required to actually connect&lt;br /&gt;
to those servers.&lt;br /&gt;
&lt;br /&gt;
In previous test using perl, the individual PV search actually used an LDIF&lt;br /&gt;
filter with exact match like 'testrec0000000001'. In addition, all PVs matching&lt;br /&gt;
a filter 'testrec*' were retrieved, and no index was used:&lt;br /&gt;
 Adding   1000 records:  4 wallclock secs ( 2.29 usr +  0.14 sys =  2.43 CPU)&lt;br /&gt;
 Locating 1000 records: 49 wallclock secs ( 4.10 usr +  0.21 sys =  4.31 CPU)&lt;br /&gt;
 Match all        1000:  1 wallclock secs ( 1.18 usr +  0.02 sys =  1.20 CPU)&lt;br /&gt;
 Delete   1000 records:  4 wallclock secs ( 1.35 usr +  0.14 sys =  1.49 CPU)&lt;br /&gt;
&lt;br /&gt;
When locating individual records via the complete LDIF base without a filter,&lt;br /&gt;
the search is much faster:&lt;br /&gt;
Locating 1000 records:  5 wallclock secs ( 3.20 usr +  0.18 sys =  3.38 CPU)&lt;br /&gt;
&lt;br /&gt;
When adding an &amp;quot;index cn pres,eq&amp;quot; to the server config, the results are the&lt;br /&gt;
same for both the filter and the base search case.&lt;br /&gt;
No difference observed when tripling the cachesize from the default of 1000.&lt;br /&gt;
The behavior scales linearly with the number of channels,&lt;br /&gt;
always a little over 200 additions or lookups per second:&lt;br /&gt;
 Adding   10000 records: 47 wallclock secs (21.72 usr +  1.28 sys = 23.00 CPU)&lt;br /&gt;
 Locating 10000 records: 44 wallclock secs (32.20 usr +  1.55 sys = 33.75 CPU)&lt;br /&gt;
 Match all        10000: 15 wallclock secs (12.15 usr +  0.64 sys = 12.79 CPU)&lt;br /&gt;
 Delete   10000 records: 38 wallclock secs (15.41 usr +  1.16 sys = 16.57 CPU)&lt;br /&gt;
&lt;br /&gt;
 Adding   50000 records: 243 wallclock secs (110.36 usr +  7.18 sys = 117.54 CPU)&lt;br /&gt;
 Locating 50000 records: 226 wallclock secs (164.97 usr +  7.67 sys = 172.64 CPU)&lt;br /&gt;
 Match all        50000: 80 wallclock secs (63.09 usr +  3.42 sys = 66.51 CPU)&lt;br /&gt;
 Delete   50000 records: 219 wallclock secs (82.11 usr +  6.61 sys = 88.72 CPU)&lt;br /&gt;
&lt;br /&gt;
 Adding  200000 records: 1293 wallclock secs (483.65 usr + 33.20 sys = 516.85 CPU)&lt;br /&gt;
&lt;br /&gt;
&amp;quot;index cn,dc pres,eq,approx,sub&amp;quot; takes longer to insert/delete&lt;br /&gt;
without improved search performance:&lt;br /&gt;
 Adding   10000 records: 104 wallclock secs (26.25 usr +  1.67 sys = 27.92 CPU)&lt;br /&gt;
 Locating 10000 records: 45 wallclock secs (32.86 usr +  1.55 sys = 34.41 CPU)&lt;br /&gt;
 Match all        10000: 15 wallclock secs (12.31 usr +  0.47 sys = 12.78 CPU)&lt;br /&gt;
 Delete   10000 records: 97 wallclock secs (17.89 usr +  1.34 sys = 19.23 CPU)&lt;br /&gt;
&lt;br /&gt;
The CPU load is a rough 50/50 split between the perl and slapd processes.&lt;br /&gt;
The perl ldap library is 100% perl, including the network handling and&lt;br /&gt;
quite some data conversions from arrays into hashes and back,&lt;br /&gt;
so a pure C client might be a little faster.&lt;br /&gt;
&lt;br /&gt;
Though an index slows down the insertion of PVs, it speeds up certain&lt;br /&gt;
retrieval methods, as probably desired for an EPICS directory server.&lt;br /&gt;
The search mechanism is based on round-trip requests.&lt;br /&gt;
Searching PVs one PV name at a time is slower than requesting all&lt;br /&gt;
PVs that match a pattern. Unfortunately, typical CA clients will&lt;br /&gt;
have to request data for specific PV names, not by pattern.&lt;br /&gt;
&lt;br /&gt;
=== To be investigated ===&lt;br /&gt;
&lt;br /&gt;
* How to access it from vxWorks&lt;br /&gt;
* How does an LDAP server used for EPICS cooperate with existing LDAP servers for DNS, email etc. Should it be one and the same? Use special port numbers for 'EPICS' LDAP?&lt;br /&gt;
* API: What type of API would EPICS tools use? Whatever LDAP library they use? An 'EPICS wrapper' around LDAP? Gasper suggested to look at the Java JNDI API for inspiration.&lt;br /&gt;
* Common Database headaches:&lt;br /&gt;
** To simply get a PV into LDAP, one has to check if this PV already exists and then either 'add' or 'modify'.&lt;br /&gt;
** What if a channel is no longer available? An IOC shutting down could remove PVs from the LDAP server, but an IOC that crashes won't.&lt;br /&gt;
* Replication: How to use it, how fast is it etc.&lt;br /&gt;
* Authentication and encryption: How hard is it to configure?&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=360</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=360"/>
		<updated>2005-06-29T15:42:43Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: channel::getProperties?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
* getProperties()&amp;lt;br&amp;gt;A read or subscription will return a dataAccess interface to the retrieved data. That might exclude properties that this client cannot access. It will include the data, which might be huge. This separate call will only get property information, no data, for all properties, including read/write access information. The result could be in the form of dataAccess, except all access to actual data yields empty results, only the property-info related part of dataAccess is functional.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types?&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* disconnected(Channel ch)&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;property&amp;gt; what, Event e, Filter f)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
** event is a set of any of the following:&lt;br /&gt;
*** exceeded absolute value deadband, exceeded percentage change, exceeded logarithmic change&lt;br /&gt;
*** minimum update period expired&lt;br /&gt;
*** alarm condition change&lt;br /&gt;
*** some hardware event (like blue beam)&lt;br /&gt;
** filters&lt;br /&gt;
*** override server-side idea of the value deadband, percentage change, log. change, minumum update rate, maximum update rate&lt;br /&gt;
*** specify value count&amp;lt;br&amp;gt;setting this to '1' turns the subscription into a single-value 'get'&lt;br /&gt;
* cancelSubscription(Event, Filter)&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read', 'write'.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=331</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=331"/>
		<updated>2005-06-27T19:44:33Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: /* Directory */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
There is a default directory, available via something like &amp;quot;EPICSDefaults::getDirectory()&amp;quot; and &amp;quot;...setDirectory()&amp;quot;.&lt;br /&gt;
The implementation could use&lt;br /&gt;
* LDAP&lt;br /&gt;
* Broadcast (as before)&lt;br /&gt;
* your custom implementation&amp;lt;br&amp;gt;as long as it uses the above interface and registers itself as the default.&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client us&lt;br /&gt;
er.&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types? &lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* disconnected(Channel ch)&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;property&amp;gt; what, Event e, Filter f)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
** event is a set of any of the following:&lt;br /&gt;
*** exceeded absolute value deadband, exceeded percentage change, exceeded logarithmic change&lt;br /&gt;
*** minimum update period expired&lt;br /&gt;
*** alarm condition change&lt;br /&gt;
*** some hardware event (like blue beam)&lt;br /&gt;
** filters&lt;br /&gt;
*** override server-side idea of the value deadband, percentage change, log. change, minumum update rate, maximum update rate&lt;br /&gt;
*** specify value count&amp;lt;br&amp;gt;setting this to '1' turns the subscription into a single-value 'get'&lt;br /&gt;
* cancelSubscription(Event, Filter)&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read', 'write'.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Name_Server&amp;diff=332</id>
		<title>V4 Name Server</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Name_Server&amp;diff=332"/>
		<updated>2005-06-27T19:33:32Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: defaults, properties&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Directory Server ==&lt;br /&gt;
&lt;br /&gt;
EPICS ChannelAccess currently uses a broadcast mechanism for resolving&lt;br /&gt;
channel names.&lt;br /&gt;
Advantages:&lt;br /&gt;
* Minimal if not zero initial setup.&lt;br /&gt;
* At least initially very fast, because a client can send multiple name requests in one network packet, all servers receive it simultaneously and can answer to known names.&lt;br /&gt;
&lt;br /&gt;
Disadvantages:&lt;br /&gt;
* Unclear how to extend this service to redundant servers, archived data, ...&lt;br /&gt;
* Currently, no wildcard searches.&lt;br /&gt;
* Currently, no statistics on available number of channels, types of records, ...&lt;br /&gt;
* Bigger installations run into problems with broadcast traffic:&lt;br /&gt;
** All devices see the requests, even though only one server has a given channel name.&lt;br /&gt;
** Continued searches for unresolved names to overcome UDP limitations.&lt;br /&gt;
&lt;br /&gt;
As Larry Hoff pointed out, we really want a ''Directory'' server for&lt;br /&gt;
EPICS, something beyond a name server. A directory will not only map PV&lt;br /&gt;
names to IP addresses of ChannelAccess servers, but also support&lt;br /&gt;
* wildcard searches, reports on number of channels per IOC etc.&lt;br /&gt;
* redundant servers&lt;br /&gt;
* info about reduntant IOCs with the same PV, archived data for a PV&lt;br /&gt;
* backup/restore locations for a PV&lt;br /&gt;
* PV meta information like engineer-in-charge, CA security rules, ...&lt;br /&gt;
&lt;br /&gt;
== Channels, Records, PVs, Properties ==&lt;br /&gt;
We currently have records with fields and channels with properties.&lt;br /&gt;
Each field of a record can become a channel name,&lt;br /&gt;
the field itself ends up in the 'value' property of the channel,&lt;br /&gt;
and the record decides how to fill the remaining properties.&lt;br /&gt;
&lt;br /&gt;
Will the directory list all record names or all possible channels?&lt;br /&gt;
I think only record names.&lt;br /&gt;
&lt;br /&gt;
Will it include the record type? Not enough information if we add user-defined fields.&lt;br /&gt;
Will it lists all fields of a record?&lt;br /&gt;
&lt;br /&gt;
I think for now that only listing record names is enough in order to allow&lt;br /&gt;
connections.&lt;br /&gt;
&lt;br /&gt;
The CA client would then have to query the CA server for all the available properties&lt;br /&gt;
of a PV, and note that properties might even change at runtime when we allow&lt;br /&gt;
online add &amp;amp; delete.&lt;br /&gt;
&lt;br /&gt;
Whenever a tool like a command-line 'caget' or the 'probe' GUI requires a PV name,&lt;br /&gt;
the convention might be that &amp;quot;fred&amp;quot; will address all properties of the channel &amp;quot;fred&amp;quot;.&lt;br /&gt;
In order to get the value, one would have to use &amp;quot;fred.value&amp;quot;.&lt;br /&gt;
Note that the &amp;quot;fred&amp;quot; == &amp;quot;fred.VAL&amp;quot; default would no longer apply.&lt;br /&gt;
This change would scale better when accessing hierarchical properties:&lt;br /&gt;
&amp;quot;fred.limits&amp;quot; would get all limits, while &amp;quot;fred.limits.display.high&amp;quot; would only get&lt;br /&gt;
the requested element.&lt;br /&gt;
&lt;br /&gt;
== LDAP, Directory, RDB, Schema, Entries, dn, cn, dc, ... ==&lt;br /&gt;
&lt;br /&gt;
The ''Lightweight Directory Access Protocol'' (LDAP) is a network protocol.&lt;br /&gt;
It defines how to access data over the network, regardless of language&lt;br /&gt;
or underlying data storage. In contrast, RDBs like Oracle or MySQL define the&lt;br /&gt;
data storage and SQL dialect, while the network access to the data is then&lt;br /&gt;
added almost as an afterthought.&lt;br /&gt;
&lt;br /&gt;
LDAP is meant as a Directory for simple attribute-based information&lt;br /&gt;
for fast, high volume lookup. Simple search filters are supported:&lt;br /&gt;
Patterns, &amp;quot;and&amp;quot;, &amp;quot;or&amp;quot;. In contrast to RDBs, there is no support for&lt;br /&gt;
arbitrary relations between entries, there are no transactions nor rollback.&lt;br /&gt;
&lt;br /&gt;
There are open-source servers for LDAP, and client libraries for&lt;br /&gt;
C, Perl, Java and other languages. MacOSX, Linux, Windows 2000&lt;br /&gt;
already include LDAP clients. MS ActiveDirectory uses LDAP.&lt;br /&gt;
&lt;br /&gt;
LDAP requires the data to be organized into hierarchical ''Entries''.&lt;br /&gt;
Each Entry has a globally unique ''Distinguished Name'' (dn),&lt;br /&gt;
and various ''Attributes'', that is Type/Value pairs.&lt;br /&gt;
The ''Schema'' defines which attributes an entry must have and which ones&lt;br /&gt;
it might have. There are existing schemas for DNS-type data or personell&lt;br /&gt;
information, including types like ''Common Name'' (cn) or ''Domain Component'' (dc),&lt;br /&gt;
but one can add custom schemata. Values might include binary data, e.g. images.&lt;br /&gt;
&lt;br /&gt;
=== Mapping EPICS PV Information to LDAP ===&lt;br /&gt;
&lt;br /&gt;
Each LDAP entry must have a unique name (dn). EPICS PVs must be unique as well,&lt;br /&gt;
but only within the search domain currently defined by the EPICS_CA_ADDR_LIST.&lt;br /&gt;
It might make sense to synchronize LDAP servers across these boundaries,&lt;br /&gt;
in which case the LDAP dn will have to include more than just the PV name.&lt;br /&gt;
For example, one could have a PV &amp;quot;fred&amp;quot; in both the accelerator and office network&lt;br /&gt;
of the SNS, and enter them into an SNS-wide LDAP directory like this:&lt;br /&gt;
&lt;br /&gt;
 fred.linac.sns.epics:  cas=&amp;quot;linacioc1:5065&amp;quot;&lt;br /&gt;
 fred.office.sns.epics: cas=&amp;quot;testioc47.sns.ornl.gov:7987&amp;quot;, description=&amp;quot;Fred's test PV&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In this example, the directory includes the ChannelAccess server for both PVs,&lt;br /&gt;
and in one case also a description.&lt;br /&gt;
&lt;br /&gt;
While an EPICS-specific tool might present the data as shown above,&lt;br /&gt;
most generic LDAP tools and APIs use the ''LDAP Data Interchange Format'' (LDIF).&lt;br /&gt;
In LDIF, part of the above data could look like this:&lt;br /&gt;
&lt;br /&gt;
 dn: dc=epics&lt;br /&gt;
 objectclass: dcObject&lt;br /&gt;
 objectclass: organization&lt;br /&gt;
 o: Root of all EPICS PVs&lt;br /&gt;
 dc: epics&lt;br /&gt;
&lt;br /&gt;
 dn: dc=sns,dc=epics&lt;br /&gt;
 # Details omitted; similar to previous entry&lt;br /&gt;
&lt;br /&gt;
 dn: dc=linac,dc=sns,dc=epics&lt;br /&gt;
 objectclass: dcObject&lt;br /&gt;
 objectclass: organization&lt;br /&gt;
 o: Root of all EPICS PVs in the SNS Linac&lt;br /&gt;
 dc: linac&lt;br /&gt;
&lt;br /&gt;
 dn: cn=fred,dc=linac,dc=sns,dc=epics&lt;br /&gt;
 objectclass: ProcessVariable&lt;br /&gt;
 cn: fred&lt;br /&gt;
 cas: linacioc1:5065 &lt;br /&gt;
&lt;br /&gt;
Some points I had to learn:&lt;br /&gt;
* Each entry has an objectclass. The schema for &amp;quot;ProcessVariable&amp;quot; would for example require a &amp;quot;cn&amp;quot; attribute, with optional &amp;quot;cas&amp;quot;, &amp;quot;description&amp;quot; and maybe other attributes.&amp;lt;br&amp;gt;The schema would also determine if searches for PV names are case-sensitive or not.&lt;br /&gt;
* The hierarchical path &amp;quot;fred.linac.sns.epics&amp;quot; translates into a dn &amp;quot;cn=fred,dc=linac,dc=sns,dc=epics&amp;quot; which lists the relative dn of the entry itself and all path elements.&lt;br /&gt;
* Each path element must exists, so to bootstrap one has to create entries &amp;quot;dc=epics&amp;quot; and &amp;quot;dc=sns,dc=epics&amp;quot; etc., for which I used the pre-defined &amp;quot;organization&amp;quot; schema.&lt;br /&gt;
* To locate the PV 'fred', any of the following LDAP searches would work:&lt;br /&gt;
** Scope &amp;quot;base&amp;quot; with base &amp;quot;cn=fred,dc=linac,dc=sns,dc=epics&amp;quot;.&lt;br /&gt;
** Scope &amp;quot;one&amp;quot; with base &amp;quot;dc=linac,dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=fred)&amp;quot;.&lt;br /&gt;
** Scope &amp;quot;one&amp;quot; with base &amp;quot;dc=linac,dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=f*d)&amp;quot;,&amp;lt;br&amp;gt;though this might return more records which happen to match the pattern.&lt;br /&gt;
** Scope &amp;quot;sub&amp;quot; with base &amp;quot;dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=fred)&amp;quot;,&amp;lt;br&amp;gt;which would also return an entry with dn &amp;quot;cn=fred,dc=office,dc=sns,dc=epics&amp;quot;.&lt;br /&gt;
** There is a filter option &amp;quot;~=&amp;quot; for 'approximately equal'. Unclear what exactly defines approximate, but this might be very handy for detecing typos.&lt;br /&gt;
&lt;br /&gt;
=== OpenLDAP ===&lt;br /&gt;
&lt;br /&gt;
OpenLDAP  [http://www.openldap.org] is an open-source LDAP&lt;br /&gt;
client library for C and an LDAP server which can store the&lt;br /&gt;
actual data in /dev/null, a BerkeleyDB, SQL, Perl, a remote LDAP server.&lt;br /&gt;
&lt;br /&gt;
OpenLDAP includes authentication and encrypted (SSL) transport,&lt;br /&gt;
replication (single master -&amp;gt; multiple slaves),&lt;br /&gt;
and referrals from one server to another one which has more details.&lt;br /&gt;
&lt;br /&gt;
=== EPICS PV Tests ===&lt;br /&gt;
&lt;br /&gt;
Using OpenLDAP-stable-20050429 and perl-ldap-0.3202 on a 1.3 GHz PowerBook G4&lt;br /&gt;
with 780 MB RAM under MacOS X 10.3.8, I used a perl script to create&lt;br /&gt;
PV entries, search them, and remove them.&lt;br /&gt;
In addition, the 'ldapsearch' that comes with RedHat Enterprise AS 3 was&lt;br /&gt;
used to check remote accessibillity.&lt;br /&gt;
&lt;br /&gt;
When trying to list all PVs with an appropriate search pattern,&lt;br /&gt;
the default server configuration limits the response to 500 answers.&lt;br /&gt;
This was changed such that 'paged' searches which keep requesting data&lt;br /&gt;
in increments of e.g. 500 entries are allowed to continue until all data is retrieved.&lt;br /&gt;
From the command-line tool, that's done like this:&lt;br /&gt;
 ldapsearch -x -LLL -E pr=10/noprompt -b 'dc=epics' '(cn=testpv*)'  cas&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Based on the default OpenLDAP server config with an additional&lt;br /&gt;
index &amp;quot;cn pres,eq&amp;quot; on the common name of the PVs,&lt;br /&gt;
PVs with names 'testrec0000000001', 'testrec0000000002' etc.&lt;br /&gt;
were added, then looked up one-by-one via their exact name,&lt;br /&gt;
and finially deleted one by one.&lt;br /&gt;
&lt;br /&gt;
When using a C++ test client based on the OpenLDAP library,&lt;br /&gt;
the results for smaller numbers are:&lt;br /&gt;
 Add         10000 records : 22.76 secs&lt;br /&gt;
 Locate      10000 records : 6.49 secs&lt;br /&gt;
 Delete      10000 records : 22.20 secs&lt;br /&gt;
&lt;br /&gt;
In another test, a very rough estimated PV count of the SNS linac&lt;br /&gt;
was used:&lt;br /&gt;
 Add        200000 records : 675.89 secs (38.74 CPU)&lt;br /&gt;
 Locate     200000 records : 142.85 secs (27.91 CPU)&lt;br /&gt;
 Delete     200000 records : 807.28 secs (37.28 CPU)&lt;br /&gt;
&lt;br /&gt;
Those 200000 records use about 900MB of file space.&lt;br /&gt;
The results indicate that one can add 300 .. 440 records/sec,&lt;br /&gt;
locate 1400.. 1540 records/sec and delete 250..450 records/sec.&lt;br /&gt;
Without an index, the addition and deletion will be faster&lt;br /&gt;
at the expense of lookup times.&lt;br /&gt;
&lt;br /&gt;
A typical SNS LLRF IOCs has 2300 records.&lt;br /&gt;
On startup, sending those to the directory server would take around 7 seconds.&lt;br /&gt;
A typical SNS LLRF overview screen has about 400 PVs,&lt;br /&gt;
and current connection times vary from less than a second to&lt;br /&gt;
several tens of seconds, depending on how well the search requests&lt;br /&gt;
reach the IOCs.&lt;br /&gt;
Resolving their CA servers would take about 0.3 seconds with LDAP,&lt;br /&gt;
which would then be followed by the time required to actually connect&lt;br /&gt;
to those servers.&lt;br /&gt;
&lt;br /&gt;
In previous test using perl, the individual PV search actually used an LDIF&lt;br /&gt;
filter with exact match like 'testrec0000000001'. In addition, all PVs matching&lt;br /&gt;
a filter 'testrec*' were retrieved, and no index was used:&lt;br /&gt;
 Adding   1000 records:  4 wallclock secs ( 2.29 usr +  0.14 sys =  2.43 CPU)&lt;br /&gt;
 Locating 1000 records: 49 wallclock secs ( 4.10 usr +  0.21 sys =  4.31 CPU)&lt;br /&gt;
 Match all        1000:  1 wallclock secs ( 1.18 usr +  0.02 sys =  1.20 CPU)&lt;br /&gt;
 Delete   1000 records:  4 wallclock secs ( 1.35 usr +  0.14 sys =  1.49 CPU)&lt;br /&gt;
&lt;br /&gt;
When locating individual records via the complete LDIF base without a filter,&lt;br /&gt;
the search is much faster:&lt;br /&gt;
Locating 1000 records:  5 wallclock secs ( 3.20 usr +  0.18 sys =  3.38 CPU)&lt;br /&gt;
&lt;br /&gt;
When adding an &amp;quot;index cn pres,eq&amp;quot; to the server config, the results are the&lt;br /&gt;
same for both the filter and the base search case.&lt;br /&gt;
No difference observed when tripling the cachesize from the default of 1000.&lt;br /&gt;
The behavior scales linearly with the number of channels,&lt;br /&gt;
always a little over 200 additions or lookups per second:&lt;br /&gt;
 Adding   10000 records: 47 wallclock secs (21.72 usr +  1.28 sys = 23.00 CPU)&lt;br /&gt;
 Locating 10000 records: 44 wallclock secs (32.20 usr +  1.55 sys = 33.75 CPU)&lt;br /&gt;
 Match all        10000: 15 wallclock secs (12.15 usr +  0.64 sys = 12.79 CPU)&lt;br /&gt;
 Delete   10000 records: 38 wallclock secs (15.41 usr +  1.16 sys = 16.57 CPU)&lt;br /&gt;
&lt;br /&gt;
 Adding   50000 records: 243 wallclock secs (110.36 usr +  7.18 sys = 117.54 CPU)&lt;br /&gt;
 Locating 50000 records: 226 wallclock secs (164.97 usr +  7.67 sys = 172.64 CPU)&lt;br /&gt;
 Match all        50000: 80 wallclock secs (63.09 usr +  3.42 sys = 66.51 CPU)&lt;br /&gt;
 Delete   50000 records: 219 wallclock secs (82.11 usr +  6.61 sys = 88.72 CPU)&lt;br /&gt;
&lt;br /&gt;
 Adding  200000 records: 1293 wallclock secs (483.65 usr + 33.20 sys = 516.85 CPU)&lt;br /&gt;
&lt;br /&gt;
&amp;quot;index cn,dc pres,eq,approx,sub&amp;quot; takes longer to insert/delete&lt;br /&gt;
without improved search performance:&lt;br /&gt;
 Adding   10000 records: 104 wallclock secs (26.25 usr +  1.67 sys = 27.92 CPU)&lt;br /&gt;
 Locating 10000 records: 45 wallclock secs (32.86 usr +  1.55 sys = 34.41 CPU)&lt;br /&gt;
 Match all        10000: 15 wallclock secs (12.31 usr +  0.47 sys = 12.78 CPU)&lt;br /&gt;
 Delete   10000 records: 97 wallclock secs (17.89 usr +  1.34 sys = 19.23 CPU)&lt;br /&gt;
&lt;br /&gt;
The CPU load is a rough 50/50 split between the perl and slapd processes.&lt;br /&gt;
The perl ldap library is 100% perl, including the network handling and&lt;br /&gt;
quite some data conversions from arrays into hashes and back,&lt;br /&gt;
so a pure C client might be a little faster.&lt;br /&gt;
&lt;br /&gt;
Though an index slows down the insertion of PVs, it speeds up certain&lt;br /&gt;
retrieval methods, as probably desired for an EPICS directory server.&lt;br /&gt;
The search mechanism is based on round-trip requests.&lt;br /&gt;
Searching PVs one PV name at a time is slower than requesting all&lt;br /&gt;
PVs that match a pattern. Unfortunately, typical CA clients will&lt;br /&gt;
have to request data for specific PV names, not by pattern.&lt;br /&gt;
&lt;br /&gt;
=== To be investigated ===&lt;br /&gt;
&lt;br /&gt;
* How to access it from vxWorks&lt;br /&gt;
* How does an LDAP server used for EPICS cooperate with existing LDAP servers for DNS, email etc. Should it be one and the same? Use special port numbers for 'EPICS' LDAP?&lt;br /&gt;
* API: What type of API would EPICS tools use? Whatever LDAP library they use? An 'EPICS wrapper' around LDAP? Gasper suggested to look at the Java JNDI API for inspiration.&lt;br /&gt;
* Common Database headaches:&lt;br /&gt;
** To simply get a PV into LDAP, one has to check if this PV already exists and then either 'add' or 'modify'.&lt;br /&gt;
** What if a channel is no longer available? An IOC shutting down could remove PVs from the LDAP server, but an IOC that crashes won't.&lt;br /&gt;
* Replication: How to use it, how fast is it etc.&lt;br /&gt;
* Authentication and encryption: How hard is it to configure?&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=328</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=328"/>
		<updated>2005-06-27T19:14:28Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: how to get property info?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client user.&lt;br /&gt;
&lt;br /&gt;
See separate  [[V4 Name Server]] wiki for the issue of &amp;quot;record&amp;quot; and &amp;quot;PV&amp;quot; information.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
Note also that in order to read or write, one needs to know what properties to access.&lt;br /&gt;
Does the channel magically provide a list of all properties?&lt;br /&gt;
Is there a &amp;quot;get all properties&amp;quot; request that reads them on-demand&lt;br /&gt;
from the CA server?&lt;br /&gt;
Or does the directory hold the list of available properties and their types? &lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* disconnected(Channel ch)&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;property&amp;gt; what, Event e, Filter f)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
** event is a set of any of the following:&lt;br /&gt;
*** exceeded absolute value deadband, exceeded percentage change, exceeded logarithmic change&lt;br /&gt;
*** minimum update period expired&lt;br /&gt;
*** alarm condition change&lt;br /&gt;
*** some hardware event (like blue beam)&lt;br /&gt;
** filters&lt;br /&gt;
*** override server-side idea of the value deadband, percentage change, log. change, minumum update rate, maximum update rate&lt;br /&gt;
*** specify value count&amp;lt;br&amp;gt;setting this to '1' turns the subscription into a single-value 'get'&lt;br /&gt;
* cancelSubscription(Event, Filter)&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read', 'write'.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=326</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=326"/>
		<updated>2005-06-27T15:48:54Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: scheduled write&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client user.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* disconnected(Channel ch)&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* scheduleWriteRequest(new value, event)&amp;lt;BR&amp;gt;Sends the value to the server, which will perform the write when the given event fires. Invokes Listener when CA server has performed the write.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;property&amp;gt; what, Event e, Filter f)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
** event is a set of any of the following:&lt;br /&gt;
*** exceeded absolute value deadband, exceeded percentage change, exceeded logarithmic change&lt;br /&gt;
*** minimum update period expired&lt;br /&gt;
*** alarm condition change&lt;br /&gt;
*** some hardware event (like blue beam)&lt;br /&gt;
** filters&lt;br /&gt;
*** override server-side idea of the value deadband, percentage change, log. change, minumum update rate, maximum update rate&lt;br /&gt;
*** specify value count&amp;lt;br&amp;gt;setting this to '1' turns the subscription into a single-value 'get'&lt;br /&gt;
* cancelSubscription(Event, Filter)&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read', 'write'.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=324</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=324"/>
		<updated>2005-06-27T15:23:35Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Ben: hide&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client user.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
&lt;br /&gt;
Note that Ben suggests that the CA client library should select the CA server automatically by querying a server or via broadcasts; it should also pick amongst competing PVs of the same name without user interaction.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* disconnected(Channel ch)&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;property&amp;gt; what, Event e, Filter f)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
** event is a set of any of the following:&lt;br /&gt;
*** exceeded absolute value deadband, exceeded percentage change, exceeded logarithmic change&lt;br /&gt;
*** minimum update period expired&lt;br /&gt;
*** alarm condition change&lt;br /&gt;
*** some hardware event (like blue beam)&lt;br /&gt;
** filters&lt;br /&gt;
*** override server-side idea of the value deadband, percentage change, log. change, minumum update rate, maximum update rate&lt;br /&gt;
*** specify value count&amp;lt;br&amp;gt;setting this to '1' turns the subscription into a single-value 'get'&lt;br /&gt;
* cancelSubscription(Event, Filter)&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read', 'write'.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=323</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=323"/>
		<updated>2005-06-27T15:21:30Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Ben: hide&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
Note that Ben argues to hide the directory lookup from the CA client user.&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* disconnected(Channel ch)&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;property&amp;gt; what, Event e, Filter f)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
** event is a set of any of the following:&lt;br /&gt;
*** exceeded absolute value deadband, exceeded percentage change, exceeded logarithmic change&lt;br /&gt;
*** minimum update period expired&lt;br /&gt;
*** alarm condition change&lt;br /&gt;
*** some hardware event (like blue beam)&lt;br /&gt;
** filters&lt;br /&gt;
*** override server-side idea of the value deadband, percentage change, log. change, minumum update rate, maximum update rate&lt;br /&gt;
*** specify value count&amp;lt;br&amp;gt;setting this to '1' turns the subscription into a single-value 'get'&lt;br /&gt;
* cancelSubscription(Event, Filter)&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read', 'write'.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=322</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=322"/>
		<updated>2005-06-24T16:07:22Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Ideas for the event &amp;amp; Filter. Separate &amp;quot;SyncChannel&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory ===&lt;br /&gt;
The directory is used to discover available channels and map them to the CA Server address and port.&lt;br /&gt;
* Directory(string URL-type-server-address = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Constructor; uses site-specific default server or specific one. The 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel ===&lt;br /&gt;
* Channel(string name, Directory dir = default)&amp;lt;br&amp;gt;Constructor. Channel has to have a name. Uses the 'best' channel from the default directory or from a given directory.&lt;br /&gt;
* Channel(string name, CASInfo)&amp;lt;br&amp;gt;... in case you want a specific server after querying the directory yourself or not using a directory at all.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
* addListener(Listener l), removeListener(Listener l)&amp;lt;br&amp;gt;Register for notifications, see below.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Listener ===&lt;br /&gt;
The Channel::Listener interface is invoked in response to the many asynchronous methods of the Channel.&lt;br /&gt;
* writeComplete(Channel ch)&lt;br /&gt;
** Channel that send this notification&lt;br /&gt;
* newData(Channel ch, DataAccess data, Event why)&lt;br /&gt;
** DataAccess interface to the data&lt;br /&gt;
** Reason for the update: Value deadband exceeded, minimum period expired, event 'blue beam', ...&lt;br /&gt;
* disconnected(Channel ch)&lt;br /&gt;
* accessRightChange(Channel ch)&lt;br /&gt;
&lt;br /&gt;
Alternatively, these could be separate interfaces for a writeListener, dataListener, stateListener.&lt;br /&gt;
&lt;br /&gt;
=== Channel: State ===&lt;br /&gt;
In contrast to the V3 API, the user doesn't 'connect'.&lt;br /&gt;
The first data notification implies that we're connected.&lt;br /&gt;
&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Writing ===&lt;br /&gt;
* write(new value)&amp;lt;BR&amp;gt;Sends the value to the server.&lt;br /&gt;
* createWriteRequest(new value, receipt {delivery, completion})&amp;lt;BR&amp;gt;Sends the value to the server, invokes Listener when CA server has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
&lt;br /&gt;
=== Channel: Reading ===&lt;br /&gt;
* createSubscription(list&amp;lt;property&amp;gt; what, Event e, Filter f)&amp;lt;BR&amp;gt;Will invoke Listener once data arrives.&lt;br /&gt;
** event is a set of any of the following:&lt;br /&gt;
*** exceeded absolute value deadband, exceeded percentage change, exceeded logarithmic change&lt;br /&gt;
*** minimum update period expired&lt;br /&gt;
*** alarm condition change&lt;br /&gt;
*** some hardware event (like blue beam)&lt;br /&gt;
** filters&lt;br /&gt;
*** override server-side idea of the value deadband, percentage change, log. change, minumum update rate, maximum update rate&lt;br /&gt;
*** specify value count&amp;lt;br&amp;gt;setting this to '1' turns the subscription into a single-value 'get'&lt;br /&gt;
* cancelSubscription(Event, Filter)&lt;br /&gt;
&lt;br /&gt;
=== SyncChannel ===&lt;br /&gt;
A wrapper class around the async. Channel.&lt;br /&gt;
Uses a configurable timeout and provides synchronous 'read', 'write'.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 SyncChannel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.read(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=320</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=320"/>
		<updated>2005-06-24T14:45:14Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: /* Data Access */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory Server ===&lt;br /&gt;
* setServer(string URL-type-server-address)&amp;lt;BR&amp;gt;Optional; otherwise some site-specific default name server is used. That 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(using hash IDs instead property name string etc.):&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel, General Methods ===&lt;br /&gt;
* Channel(string name)&amp;lt;br&amp;gt;Constructor. Channel has to have a name.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
&lt;br /&gt;
=== Channel, Connection Related Methods ===&lt;br /&gt;
One idea is that - in contrast to the V3 API - the user shouldn't have to connect at all.&lt;br /&gt;
The first 'get' or 'put' will try to connect.&lt;br /&gt;
A disconnect is a special type of data notification, not a separate&lt;br /&gt;
connection callback.&lt;br /&gt;
&lt;br /&gt;
* setDirectory(string directory_URL)&amp;lt;br&amp;gt;Use specific directory server, otherwise the default one is used.&lt;br /&gt;
* setServer(CA server info)&amp;lt;BR&amp;gt;Attempts to connect &amp;amp; maintain the connection with specific server. Otherwise, the directory is queried.&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel, 'Put' Related Methods ===&lt;br /&gt;
* put(new value)&amp;lt;BR&amp;gt;Sends data to the server.&amp;lt;BR&amp;gt;Returns error if currently disconnected or other local problem, no other feedback wether the value actually reached the server.&lt;br /&gt;
* putNotify(new value), putCompletionNotify(new value)&amp;lt;BR&amp;gt;Sends the value to the server, invokes putNotification when receiving record has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* addPutListener(pnl), removePutListener(pnl)&amp;lt;BR&amp;gt;Need to register listener to actually receive the put*Notify info.&lt;br /&gt;
&lt;br /&gt;
=== Channel, 'Get' Related Methods ===&lt;br /&gt;
* DataAccess = getData(property_list)&amp;lt;BR&amp;gt; Waits for the data.&lt;br /&gt;
* getDataNotify(property_list)&amp;lt;BR&amp;gt;  Will invoke DataListener once data arrives.&lt;br /&gt;
* addDataListener(dn), removeDataListener(dl)&lt;br /&gt;
&lt;br /&gt;
=== Data Listener Interface===&lt;br /&gt;
The data listener is invoked in response to an async. 'get' as well as a subscription update and contains the following:&lt;br /&gt;
* Channel that send this notification&lt;br /&gt;
* DataAccess interface to the data&lt;br /&gt;
* Reason for the update&lt;br /&gt;
** event 'x'&lt;br /&gt;
** channel is disconnected&lt;br /&gt;
** access rights changed&lt;br /&gt;
** channel moved to different IOC&lt;br /&gt;
&lt;br /&gt;
=== Channel, Subscription Related Methods ===&lt;br /&gt;
* addSubscription(event, filter)&lt;br /&gt;
** event: alarm condition change, some hardware event (like blue beam).&amp;lt;br&amp;gt;Is this still simply a bit mask, and one needs detailed info from the IOC to know that 'blue beam' == bit 14?&lt;br /&gt;
** filters: min/max period, range, rate of change, absolute change&lt;br /&gt;
* cancelSubscription(event, filter)&lt;br /&gt;
&lt;br /&gt;
The idea is that the end result of a subscription is just like a getDataNotify,&lt;br /&gt;
invoking the dataListener, except that a subscription will&lt;br /&gt;
typically return data more than once.&lt;br /&gt;
One can add more then one subscription to the same channel.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data from a 'put', 'get' or subscription.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.get(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Name_Server&amp;diff=327</id>
		<title>V4 Name Server</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_Name_Server&amp;diff=327"/>
		<updated>2005-06-23T23:08:05Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: channels or records?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Directory Server ==&lt;br /&gt;
&lt;br /&gt;
EPICS ChannelAccess currently uses a broadcast mechanism for resolving&lt;br /&gt;
channel names.&lt;br /&gt;
Advantages:&lt;br /&gt;
* Minimal if not zero initial setup.&lt;br /&gt;
* At least initially very fast, because a client can send multiple name requests in one network packet, all servers receive it simultaneously and can answer to known names.&lt;br /&gt;
&lt;br /&gt;
Disadvantages:&lt;br /&gt;
* Unclear how to extend this service to redundant servers, archived data, ...&lt;br /&gt;
* Currently, no wildcard searches.&lt;br /&gt;
* Currently, no statistics on available number of channels, types of records, ...&lt;br /&gt;
* Bigger installations run into problems with broadcast traffic:&lt;br /&gt;
** All devices see the requests, even though only one server has a given channel name.&lt;br /&gt;
** Continued searches for unresolved names to overcome UDP limitations.&lt;br /&gt;
&lt;br /&gt;
As Larry Hoff pointed out, we really want a ''Directory'' server for&lt;br /&gt;
EPICS, something beyond a name server. A directory will not only map PV&lt;br /&gt;
names to IP addresses of ChannelAccess servers, but also support&lt;br /&gt;
* wildcard searches, reports on number of channels per IOC etc.&lt;br /&gt;
* redundant servers&lt;br /&gt;
* info about reduntant IOCs with the same PV, archived data for a PV&lt;br /&gt;
* backup/restore locations for a PV&lt;br /&gt;
* PV meta information like engineer-in-charge, CA security rules, ...&lt;br /&gt;
&lt;br /&gt;
== Channels, Records, PVs ==&lt;br /&gt;
We currently have records with fields and channels with properties.&lt;br /&gt;
Each field of a record can become a channel name,&lt;br /&gt;
the field itself ends up in the 'value' property of the channel,&lt;br /&gt;
and the record decides how fill the remaining properties.&lt;br /&gt;
&lt;br /&gt;
Will the directory list all record names or all possible channels?&lt;br /&gt;
I think only record names.&lt;br /&gt;
&lt;br /&gt;
Will it include the record type? Not enough information if we add user-defined fields.&lt;br /&gt;
Will it lists all fields of a record?&lt;br /&gt;
&lt;br /&gt;
I think for now that only listing record names is enough in order to allow&lt;br /&gt;
connections.&lt;br /&gt;
&lt;br /&gt;
== LDAP, Directory, RDB, Schema, Entries, dn, cn, dc, ... ==&lt;br /&gt;
&lt;br /&gt;
The ''Lightweight Directory Access Protocol'' (LDAP) is a network protocol.&lt;br /&gt;
It defines how to access data over the network, regardless of language&lt;br /&gt;
or underlying data storage. In contrast, RDBs like Oracle or MySQL define the&lt;br /&gt;
data storage and SQL dialect, while the network access to the data is then&lt;br /&gt;
added almost as an afterthought.&lt;br /&gt;
&lt;br /&gt;
LDAP is meant as a Directory for simple attribute-based information&lt;br /&gt;
for fast, high volume lookup. Simple search filters are supported:&lt;br /&gt;
Patterns, &amp;quot;and&amp;quot;, &amp;quot;or&amp;quot;. In contrast to RDBs, there is no support for&lt;br /&gt;
arbitrary relations between entries, there are no transactions nor rollback.&lt;br /&gt;
&lt;br /&gt;
There are open-source servers for LDAP, and client libraries for&lt;br /&gt;
C, Perl, Java and other languages. MacOSX, Linux, Windows 2000&lt;br /&gt;
already include LDAP clients. MS ActiveDirectory uses LDAP.&lt;br /&gt;
&lt;br /&gt;
LDAP requires the data to be organized into hierarchical ''Entries''.&lt;br /&gt;
Each Entry has a globally unique ''Distinguished Name'' (dn),&lt;br /&gt;
and various ''Attributes'', that is Type/Value pairs.&lt;br /&gt;
The ''Schema'' defines which attributes an entry must have and which ones&lt;br /&gt;
it might have. There are existing schemas for DNS-type data or personell&lt;br /&gt;
information, including types like ''Common Name'' (cn) or ''Domain Component'' (dc),&lt;br /&gt;
but one can add custom schemata. Values might include binary data, e.g. images.&lt;br /&gt;
&lt;br /&gt;
=== Mapping EPICS PV Information to LDAP ===&lt;br /&gt;
&lt;br /&gt;
Each LDAP entry must have a unique name (dn). EPICS PVs must be unique as well,&lt;br /&gt;
but only within the search domain currently defined by the EPICS_CA_ADDR_LIST.&lt;br /&gt;
It might make sense to synchronize LDAP servers across these boundaries,&lt;br /&gt;
in which case the LDAP dn will have to include more than just the PV name.&lt;br /&gt;
For example, one could have a PV &amp;quot;fred&amp;quot; in both the accelerator and office network&lt;br /&gt;
of the SNS, and enter them into an SNS-wide LDAP directory like this:&lt;br /&gt;
&lt;br /&gt;
 fred.linac.sns.epics:  cas=&amp;quot;linacioc1:5065&amp;quot;&lt;br /&gt;
 fred.office.sns.epics: cas=&amp;quot;testioc47.sns.ornl.gov:7987&amp;quot;, description=&amp;quot;Fred's test PV&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In this example, the directory includes the ChannelAccess server for both PVs,&lt;br /&gt;
and in one case also a description.&lt;br /&gt;
&lt;br /&gt;
While an EPICS-specific tool might present the data as shown above,&lt;br /&gt;
most generic LDAP tools and APIs use the ''LDAP Data Interchange Format'' (LDIF).&lt;br /&gt;
In LDIF, part of the above data could look like this:&lt;br /&gt;
&lt;br /&gt;
 dn: dc=epics&lt;br /&gt;
 objectclass: dcObject&lt;br /&gt;
 objectclass: organization&lt;br /&gt;
 o: Root of all EPICS PVs&lt;br /&gt;
 dc: epics&lt;br /&gt;
&lt;br /&gt;
 dn: dc=sns,dc=epics&lt;br /&gt;
 # Details omitted; similar to previous entry&lt;br /&gt;
&lt;br /&gt;
 dn: dc=linac,dc=sns,dc=epics&lt;br /&gt;
 objectclass: dcObject&lt;br /&gt;
 objectclass: organization&lt;br /&gt;
 o: Root of all EPICS PVs in the SNS Linac&lt;br /&gt;
 dc: linac&lt;br /&gt;
&lt;br /&gt;
 dn: cn=fred,dc=linac,dc=sns,dc=epics&lt;br /&gt;
 objectclass: ProcessVariable&lt;br /&gt;
 cn: fred&lt;br /&gt;
 cas: linacioc1:5065 &lt;br /&gt;
&lt;br /&gt;
Some points I had to learn:&lt;br /&gt;
* Each entry has an objectclass. The schema for &amp;quot;ProcessVariable&amp;quot; would for example require a &amp;quot;cn&amp;quot; attribute, with optional &amp;quot;cas&amp;quot;, &amp;quot;description&amp;quot; and maybe other attributes.&amp;lt;br&amp;gt;The schema would also determine if searches for PV names are case-sensitive or not.&lt;br /&gt;
* The hierarchical path &amp;quot;fred.linac.sns.epics&amp;quot; translates into a dn &amp;quot;cn=fred,dc=linac,dc=sns,dc=epics&amp;quot; which lists the relative dn of the entry itself and all path elements.&lt;br /&gt;
* Each path element must exists, so to bootstrap one has to create entries &amp;quot;dc=epics&amp;quot; and &amp;quot;dc=sns,dc=epics&amp;quot; etc., for which I used the pre-defined &amp;quot;organization&amp;quot; schema.&lt;br /&gt;
* To locate the PV 'fred', any of the following LDAP searches would work:&lt;br /&gt;
** Scope &amp;quot;base&amp;quot; with base &amp;quot;cn=fred,dc=linac,dc=sns,dc=epics&amp;quot;.&lt;br /&gt;
** Scope &amp;quot;one&amp;quot; with base &amp;quot;dc=linac,dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=fred)&amp;quot;.&lt;br /&gt;
** Scope &amp;quot;one&amp;quot; with base &amp;quot;dc=linac,dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=f*d)&amp;quot;,&amp;lt;br&amp;gt;though this might return more records which happen to match the pattern.&lt;br /&gt;
** Scope &amp;quot;sub&amp;quot; with base &amp;quot;dc=sns,dc=epics&amp;quot; and filter &amp;quot;(cn=fred)&amp;quot;,&amp;lt;br&amp;gt;which would also return an entry with dn &amp;quot;cn=fred,dc=office,dc=sns,dc=epics&amp;quot;.&lt;br /&gt;
** There is a filter option &amp;quot;~=&amp;quot; for 'approximately equal'. Unclear what exactly defines approximate, but this might be very handy for detecing typos.&lt;br /&gt;
&lt;br /&gt;
=== OpenLDAP ===&lt;br /&gt;
&lt;br /&gt;
OpenLDAP  [http://www.openldap.org] is an open-source LDAP&lt;br /&gt;
client library for C and an LDAP server which can store the&lt;br /&gt;
actual data in /dev/null, a BerkeleyDB, SQL, Perl, a remote LDAP server.&lt;br /&gt;
&lt;br /&gt;
OpenLDAP includes authentication and encrypted (SSL) transport,&lt;br /&gt;
replication (single master -&amp;gt; multiple slaves),&lt;br /&gt;
and referrals from one server to another one which has more details.&lt;br /&gt;
&lt;br /&gt;
=== EPICS PV Tests ===&lt;br /&gt;
&lt;br /&gt;
Using OpenLDAP-stable-20050429 and perl-ldap-0.3202 on a 1.3 GHz PowerBook G4&lt;br /&gt;
with 780 MB RAM under MacOS X 10.3.8, I used a perl script to create&lt;br /&gt;
PV entries, search them, and remove them.&lt;br /&gt;
In addition, the 'ldapsearch' that comes with RedHat Enterprise AS 3 was&lt;br /&gt;
used to check remote accessibillity.&lt;br /&gt;
&lt;br /&gt;
When trying to list all PVs with an appropriate search pattern,&lt;br /&gt;
the default server configuration limits the response to 500 answers.&lt;br /&gt;
This was changed such that 'paged' searches which keep requesting data&lt;br /&gt;
in increments of e.g. 500 entries are allowed to continue until all data is retrieved.&lt;br /&gt;
From the command-line tool, that's done like this:&lt;br /&gt;
 ldapsearch -x -LLL -E pr=10/noprompt -b 'dc=epics' '(cn=testpv*)'  cas&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Based on the default OpenLDAP server config with an additional&lt;br /&gt;
index &amp;quot;cn pres,eq&amp;quot; on the common name of the PVs,&lt;br /&gt;
PVs with names 'testrec0000000001', 'testrec0000000002' etc.&lt;br /&gt;
were added, then looked up one-by-one via their exact name,&lt;br /&gt;
and finially deleted one by one.&lt;br /&gt;
&lt;br /&gt;
When using a C++ test client based on the OpenLDAP library,&lt;br /&gt;
the results for smaller numbers are:&lt;br /&gt;
 Add         10000 records : 22.76 secs&lt;br /&gt;
 Locate      10000 records : 6.49 secs&lt;br /&gt;
 Delete      10000 records : 22.20 secs&lt;br /&gt;
&lt;br /&gt;
In another test, a very rough estimated PV count of the SNS linac&lt;br /&gt;
was used:&lt;br /&gt;
 Add        200000 records : 675.89 secs (38.74 CPU)&lt;br /&gt;
 Locate     200000 records : 142.85 secs (27.91 CPU)&lt;br /&gt;
 Delete     200000 records : 807.28 secs (37.28 CPU)&lt;br /&gt;
&lt;br /&gt;
Those 200000 records use about 900MB of file space.&lt;br /&gt;
The results indicate that one can add 300 .. 440 records/sec,&lt;br /&gt;
locate 1400.. 1540 records/sec and delete 250..450 records/sec.&lt;br /&gt;
Without an index, the addition and deletion will be faster&lt;br /&gt;
at the expense of lookup times.&lt;br /&gt;
&lt;br /&gt;
A typical SNS LLRF IOCs has 2300 records.&lt;br /&gt;
On startup, sending those to the directory server would take around 7 seconds.&lt;br /&gt;
A typical SNS LLRF overview screen has about 400 PVs,&lt;br /&gt;
and current connection times vary from less than a second to&lt;br /&gt;
several tens of seconds, depending on how well the search requests&lt;br /&gt;
reach the IOCs.&lt;br /&gt;
Resolving their CA servers would take about 0.3 seconds with LDAP,&lt;br /&gt;
which would then be followed by the time required to actually connect&lt;br /&gt;
to those servers.&lt;br /&gt;
&lt;br /&gt;
In previous test using perl, the individual PV search actually used an LDIF&lt;br /&gt;
filter with exact match like 'testrec0000000001'. In addition, all PVs matching&lt;br /&gt;
a filter 'testrec*' were retrieved, and no index was used:&lt;br /&gt;
 Adding   1000 records:  4 wallclock secs ( 2.29 usr +  0.14 sys =  2.43 CPU)&lt;br /&gt;
 Locating 1000 records: 49 wallclock secs ( 4.10 usr +  0.21 sys =  4.31 CPU)&lt;br /&gt;
 Match all        1000:  1 wallclock secs ( 1.18 usr +  0.02 sys =  1.20 CPU)&lt;br /&gt;
 Delete   1000 records:  4 wallclock secs ( 1.35 usr +  0.14 sys =  1.49 CPU)&lt;br /&gt;
&lt;br /&gt;
When locating individual records via the complete LDIF base without a filter,&lt;br /&gt;
the search is much faster:&lt;br /&gt;
Locating 1000 records:  5 wallclock secs ( 3.20 usr +  0.18 sys =  3.38 CPU)&lt;br /&gt;
&lt;br /&gt;
When adding an &amp;quot;index cn pres,eq&amp;quot; to the server config, the results are the&lt;br /&gt;
same for both the filter and the base search case.&lt;br /&gt;
No difference observed when tripling the cachesize from the default of 1000.&lt;br /&gt;
The behavior scales linearly with the number of channels,&lt;br /&gt;
always a little over 200 additions or lookups per second:&lt;br /&gt;
 Adding   10000 records: 47 wallclock secs (21.72 usr +  1.28 sys = 23.00 CPU)&lt;br /&gt;
 Locating 10000 records: 44 wallclock secs (32.20 usr +  1.55 sys = 33.75 CPU)&lt;br /&gt;
 Match all        10000: 15 wallclock secs (12.15 usr +  0.64 sys = 12.79 CPU)&lt;br /&gt;
 Delete   10000 records: 38 wallclock secs (15.41 usr +  1.16 sys = 16.57 CPU)&lt;br /&gt;
&lt;br /&gt;
 Adding   50000 records: 243 wallclock secs (110.36 usr +  7.18 sys = 117.54 CPU)&lt;br /&gt;
 Locating 50000 records: 226 wallclock secs (164.97 usr +  7.67 sys = 172.64 CPU)&lt;br /&gt;
 Match all        50000: 80 wallclock secs (63.09 usr +  3.42 sys = 66.51 CPU)&lt;br /&gt;
 Delete   50000 records: 219 wallclock secs (82.11 usr +  6.61 sys = 88.72 CPU)&lt;br /&gt;
&lt;br /&gt;
 Adding  200000 records: 1293 wallclock secs (483.65 usr + 33.20 sys = 516.85 CPU)&lt;br /&gt;
&lt;br /&gt;
&amp;quot;index cn,dc pres,eq,approx,sub&amp;quot; takes longer to insert/delete&lt;br /&gt;
without improved search performance:&lt;br /&gt;
 Adding   10000 records: 104 wallclock secs (26.25 usr +  1.67 sys = 27.92 CPU)&lt;br /&gt;
 Locating 10000 records: 45 wallclock secs (32.86 usr +  1.55 sys = 34.41 CPU)&lt;br /&gt;
 Match all        10000: 15 wallclock secs (12.31 usr +  0.47 sys = 12.78 CPU)&lt;br /&gt;
 Delete   10000 records: 97 wallclock secs (17.89 usr +  1.34 sys = 19.23 CPU)&lt;br /&gt;
&lt;br /&gt;
The CPU load is a rough 50/50 split between the perl and slapd processes.&lt;br /&gt;
The perl ldap library is 100% perl, including the network handling and&lt;br /&gt;
quite some data conversions from arrays into hashes and back,&lt;br /&gt;
so a pure C client might be a little faster.&lt;br /&gt;
&lt;br /&gt;
Though an index slows down the insertion of PVs, it speeds up certain&lt;br /&gt;
retrieval methods, as probably desired for an EPICS directory server.&lt;br /&gt;
The search mechanism is based on round-trip requests.&lt;br /&gt;
Searching PVs one PV name at a time is slower than requesting all&lt;br /&gt;
PVs that match a pattern. Unfortunately, typical CA clients will&lt;br /&gt;
have to request data for specific PV names, not by pattern.&lt;br /&gt;
&lt;br /&gt;
=== To be investigated ===&lt;br /&gt;
&lt;br /&gt;
* How to access it from vxWorks&lt;br /&gt;
* How does an LDAP server used for EPICS cooperate with existing LDAP servers for DNS, email etc. Should it be one and the same? Use special port numbers for 'EPICS' LDAP?&lt;br /&gt;
* API: What type of API would EPICS tools use? Whatever LDAP library they use? An 'EPICS wrapper' around LDAP? Gasper suggested to look at the Java JNDI API for inspiration.&lt;br /&gt;
* Common Database headaches:&lt;br /&gt;
** To simply get a PV into LDAP, one has to check if this PV already exists and then either 'add' or 'modify'.&lt;br /&gt;
** What if a channel is no longer available? An IOC shutting down could remove PVs from the LDAP server, but an IOC that crashes won't.&lt;br /&gt;
* Replication: How to use it, how fast is it etc.&lt;br /&gt;
* Authentication and encryption: How hard is it to configure?&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=319</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=319"/>
		<updated>2005-06-23T22:55:23Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: Split DataListener off&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory Server ===&lt;br /&gt;
* setServer(string URL-type-server-address)&amp;lt;BR&amp;gt;Optional; otherwise some site-specific default name server is used. That 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(which use hash IDs instead of strings to access properties,&lt;br /&gt;
use magic copy routines ins:&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel, General Methods ===&lt;br /&gt;
* Channel(string name)&amp;lt;br&amp;gt;Constructor. Channel has to have a name.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
&lt;br /&gt;
=== Channel, Connection Related Methods ===&lt;br /&gt;
One idea is that - in contrast to the V3 API - the user shouldn't have to connect at all.&lt;br /&gt;
The first 'get' or 'put' will try to connect.&lt;br /&gt;
A disconnect is a special type of data notification, not a separate&lt;br /&gt;
connection callback.&lt;br /&gt;
&lt;br /&gt;
* setDirectory(string directory_URL)&amp;lt;br&amp;gt;Use specific directory server, otherwise the default one is used.&lt;br /&gt;
* setServer(CA server info)&amp;lt;BR&amp;gt;Attempts to connect &amp;amp; maintain the connection with specific server. Otherwise, the directory is queried.&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel, 'Put' Related Methods ===&lt;br /&gt;
* put(new value)&amp;lt;BR&amp;gt;Sends data to the server.&amp;lt;BR&amp;gt;Returns error if currently disconnected or other local problem, no other feedback wether the value actually reached the server.&lt;br /&gt;
* putNotify(new value), putCompletionNotify(new value)&amp;lt;BR&amp;gt;Sends the value to the server, invokes putNotification when receiving record has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* addPutListener(pnl), removePutListener(pnl)&amp;lt;BR&amp;gt;Need to register listener to actually receive the put*Notify info.&lt;br /&gt;
&lt;br /&gt;
=== Channel, 'Get' Related Methods ===&lt;br /&gt;
* DataAccess = getData(property_list)&amp;lt;BR&amp;gt; Waits for the data.&lt;br /&gt;
* getDataNotify(property_list)&amp;lt;BR&amp;gt;  Will invoke DataListener once data arrives.&lt;br /&gt;
* addDataListener(dn), removeDataListener(dl)&lt;br /&gt;
&lt;br /&gt;
=== Data Listener Interface===&lt;br /&gt;
The data listener is invoked in response to an async. 'get' as well as a subscription update and contains the following:&lt;br /&gt;
* Channel that send this notification&lt;br /&gt;
* DataAccess interface to the data&lt;br /&gt;
* Reason for the update&lt;br /&gt;
** event 'x'&lt;br /&gt;
** channel is disconnected&lt;br /&gt;
** access rights changed&lt;br /&gt;
** channel moved to different IOC&lt;br /&gt;
&lt;br /&gt;
=== Channel, Subscription Related Methods ===&lt;br /&gt;
* addSubscription(event, filter)&lt;br /&gt;
** event: alarm condition change, some hardware event (like blue beam).&amp;lt;br&amp;gt;Is this still simply a bit mask, and one needs detailed info from the IOC to know that 'blue beam' == bit 14?&lt;br /&gt;
** filters: min/max period, range, rate of change, absolute change&lt;br /&gt;
* cancelSubscription(event, filter)&lt;br /&gt;
&lt;br /&gt;
The idea is that the end result of a subscription is just like a getDataNotify,&lt;br /&gt;
invoking the dataListener, except that a subscription will&lt;br /&gt;
typically return data more than once.&lt;br /&gt;
One can add more then one subscription to the same channel.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data from a 'put', 'get' or subscription.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.get(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=313</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=313"/>
		<updated>2005-06-23T22:29:28Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory Server ===&lt;br /&gt;
* setServer(string URL-type-server-address)&amp;lt;BR&amp;gt;Optional; otherwise some site-specific default name server is used. That 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(which use hash IDs instead of strings to access properties,&lt;br /&gt;
use magic copy routines ins:&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel, General Methods ===&lt;br /&gt;
* Channel(string name)&amp;lt;br&amp;gt;Constructor. Channel has to have a name.&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
&lt;br /&gt;
=== Channel, Connection Related Methods ===&lt;br /&gt;
One idea is that - in contrast to the V3 API - the user shouldn't have to connect at all.&lt;br /&gt;
The first 'get' or 'put' will try to connect.&lt;br /&gt;
A disconnect is a special type of data notification, not a separate&lt;br /&gt;
connection callback.&lt;br /&gt;
&lt;br /&gt;
* setDirectory(string directory_URL)&amp;lt;br&amp;gt;Use specific directory server, otherwise the default one is used.&lt;br /&gt;
* setServer(CA server info)&amp;lt;BR&amp;gt;Attempts to connect &amp;amp; maintain the connection with specific server. Otherwise, the directory is queried.&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel, 'Put' Related Methods ===&lt;br /&gt;
* put(new value)&amp;lt;BR&amp;gt;Sends data to the server.&amp;lt;BR&amp;gt;Returns error if currently disconnected or other local problem, no other feedback wether the value actually reached the server.&lt;br /&gt;
* putNotify(new value), putCompletionNotify(new value)&amp;lt;BR&amp;gt;Sends the value to the server, invokes putNotification when receiving record has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* addPutListener(pnl), removePutListener(pnl)&amp;lt;BR&amp;gt;Need to register listener to actually receive the put*Notify info.&lt;br /&gt;
&lt;br /&gt;
=== Channel, 'Get' Related Methods ===&lt;br /&gt;
* DataAccess = getData(property_list)&amp;lt;BR&amp;gt; Waits for the data.&lt;br /&gt;
* getDataNotify(property_list)&amp;lt;BR&amp;gt;  Will invoke DataListener once data arrives.&lt;br /&gt;
* addDataListener(dn), removeDataListener(dl)&amp;lt;BR&amp;gt;  The data listener is invoked with the channel and the data access interface to the actual data.&amp;lt;br&amp;gt; In addition, does it include event information, so when it's in response to a subscription (see below), we know why the data was sent? Or is that event information embedded in DataAccess?&lt;br /&gt;
&lt;br /&gt;
=== Channel, Subscription Related Methods ===&lt;br /&gt;
* addSubscription(event, filter)&lt;br /&gt;
** event: alarm condition change, some hardware event (like blue beam).&amp;lt;br&amp;gt;Is this still simply a bit mask, and one needs detailed info from the IOC to know that 'blue beam' == bit 14?&lt;br /&gt;
** filters: min/max period, range, rate of change, absolute change&lt;br /&gt;
* cancelSubscription(event, filter)&lt;br /&gt;
&lt;br /&gt;
The idea is that the end result of a subscription is just like a getDataNotify,&lt;br /&gt;
invoking the dataListener, except that a subscription will&lt;br /&gt;
typically return data more than once.&lt;br /&gt;
One can add more then one subscription to the same channel.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data from a 'put', 'get' or subscription.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 Channel channel(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.get(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=312</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=312"/>
		<updated>2005-06-23T22:27:48Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: /* Channel, Connection Related Methods */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory Server ===&lt;br /&gt;
* setServer(string URL-type-server-address)&amp;lt;BR&amp;gt;Optional; otherwise some site-specific default name server is used. That 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(which use hash IDs instead of strings to access properties,&lt;br /&gt;
use magic copy routines ins:&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel, General Methods ===&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
&lt;br /&gt;
=== Channel, Connection Related Methods ===&lt;br /&gt;
One idea is that - in contrast to the V3 API - the user shouldn't have to connect at all.&lt;br /&gt;
The first 'get' or 'put' will try to connect.&lt;br /&gt;
A disconnect is a special type of data notification, not a separate&lt;br /&gt;
connection callback.&lt;br /&gt;
&lt;br /&gt;
* setDirectory(string directory_URL)&amp;lt;br&amp;gt;Use specific directory server, otherwise the default one is used.&lt;br /&gt;
* setServer(CA server info)&amp;lt;BR&amp;gt;Attempts to connect &amp;amp; maintain the connection with specific server. Otherwise, the directory is queried.&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel, 'Put' Related Methods ===&lt;br /&gt;
* put(new value)&amp;lt;BR&amp;gt;Sends data to the server.&amp;lt;BR&amp;gt;Returns error if currently disconnected or other local problem, no other feedback wether the value actually reached the server.&lt;br /&gt;
* putNotify(new value), putCompletionNotify(new value)&amp;lt;BR&amp;gt;Sends the value to the server, invokes putNotification when receiving record has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* addPutListener(pnl), removePutListener(pnl)&amp;lt;BR&amp;gt;Need to register listener to actually receive the put*Notify info.&lt;br /&gt;
&lt;br /&gt;
=== Channel, 'Get' Related Methods ===&lt;br /&gt;
* DataAccess = getData(property_list)&amp;lt;BR&amp;gt; Waits for the data.&lt;br /&gt;
* getDataNotify(property_list)&amp;lt;BR&amp;gt;  Will invoke DataListener once data arrives.&lt;br /&gt;
* addDataListener(dn), removeDataListener(dl)&amp;lt;BR&amp;gt;  The data listener is invoked with the channel and the data access interface to the actual data.&amp;lt;br&amp;gt; In addition, does it include event information, so when it's in response to a subscription (see below), we know why the data was sent? Or is that event information embedded in DataAccess?&lt;br /&gt;
&lt;br /&gt;
=== Channel, Subscription Related Methods ===&lt;br /&gt;
* addSubscription(event, filter)&lt;br /&gt;
** event: alarm condition change, some hardware event (like blue beam).&amp;lt;br&amp;gt;Is this still simply a bit mask, and one needs detailed info from the IOC to know that 'blue beam' == bit 14?&lt;br /&gt;
** filters: min/max period, range, rate of change, absolute change&lt;br /&gt;
* cancelSubscription(event, filter)&lt;br /&gt;
&lt;br /&gt;
The idea is that the end result of a subscription is just like a getDataNotify,&lt;br /&gt;
invoking the dataListener, except that a subscription will&lt;br /&gt;
typically return data more than once.&lt;br /&gt;
One can add more then one subscription to the same channel.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data from a 'put', 'get' or subscription.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 Channel channel;&lt;br /&gt;
 channel.connect(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.get(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
	<entry>
		<id>https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=311</id>
		<title>V4 CA Client User Interface</title>
		<link rel="alternate" type="text/html" href="https://wiki-ext.aps.anl.gov/epics/index.php?title=V4_CA_Client_User_Interface&amp;diff=311"/>
		<updated>2005-06-23T21:46:41Z</updated>

		<summary type="html">&lt;p&gt;KayKasemir: access is per connected channel and property, doesn't belong into directory&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Channel Access Client User Interface ==&lt;br /&gt;
Under V4, a ProcessVariable would no longer be limited&lt;br /&gt;
to the current properties 'value', 'units', ... but allow the users to create&lt;br /&gt;
CA servers and clients that understand new properties.&lt;br /&gt;
&lt;br /&gt;
The low-level V4 CA client API is likely to be rather complex because it&lt;br /&gt;
now needs to handle arbitrary property catalogs.&lt;br /&gt;
* There still needs to be an easy to use high-level API, not much more complex than the existing one.&lt;br /&gt;
* There needs to be access to CA from languages  like Matlab in a way that's as easy as&lt;br /&gt;
 pv = caopen('fred');&lt;br /&gt;
 value = caget(pv);&lt;br /&gt;
&lt;br /&gt;
== Food for thought ==&lt;br /&gt;
Is there anything we can learn from other communication libraries?&lt;br /&gt;
* A brief look at [[ZeroC ICE]]&lt;br /&gt;
&lt;br /&gt;
== Skeleton API ==&lt;br /&gt;
What follows is in the form of pseudo classes and associated methods&lt;br /&gt;
that a CA client API should provide.&lt;br /&gt;
&lt;br /&gt;
=== Directory Server ===&lt;br /&gt;
* setServer(string URL-type-server-address)&amp;lt;BR&amp;gt;Optional; otherwise some site-specific default name server is used. That 'URL' might contain a user &amp;amp; password, which decides if this name server connection is read-only (for OPI clients) or if writes are allowed (for IOCs that add PVs to the name server).&lt;br /&gt;
* getChannelInfo(string PV_or_pattern)&amp;lt;BR&amp;gt;Returns list of&lt;br /&gt;
** CA server - IP &amp;amp; port of server that has the PV&lt;br /&gt;
** quality   - is this the IOC, a gateway, backup/primary&lt;br /&gt;
* addChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to register their PV in directory&lt;br /&gt;
* deleteChannelInfo(string PV, CASInfo addr_and_port, CASType primary_or_backup_or...)&amp;lt;BR&amp;gt;Used by CA servers to remove their PV from directory&lt;br /&gt;
&lt;br /&gt;
=== Data Access ===&lt;br /&gt;
All the data is accessed via DataAccess, which handles the introspection&lt;br /&gt;
of arbitrary data types. It should provide the following convenience&lt;br /&gt;
routines in addition to whatever more efficient methods it might have&lt;br /&gt;
(which use hash IDs instead of strings to access properties,&lt;br /&gt;
use magic copy routines ins:&lt;br /&gt;
* list&amp;lt;string&amp;gt; getProperties()&amp;lt;BR&amp;gt;Get a list of all the properties&lt;br /&gt;
* type_info getType(string property)&lt;br /&gt;
* bool hasWriteAccess(string property)&lt;br /&gt;
* string getAsString(string property)&lt;br /&gt;
&lt;br /&gt;
=== Channel, General Methods ===&lt;br /&gt;
* getName()&amp;lt;br&amp;gt;Returns the name of the channel.&lt;br /&gt;
&lt;br /&gt;
=== Channel, Connection Related Methods ===&lt;br /&gt;
* connect(CA server info)&amp;lt;BR&amp;gt;Attempts to connect &amp;amp; maintain the connection with specific server&lt;br /&gt;
* connect(string channel_name, string name_server URL = &amp;quot;&amp;quot;)&amp;lt;BR&amp;gt;Shortcut, probably used by most applications:&amp;lt;br&amp;gt;Queries name server (default one if left empty) and connect to the 'primary' PV. If down, tries 'backup'. If 'primary' resurfaces, switches back to it etc.&lt;br /&gt;
* disconnect()&amp;lt;BR&amp;gt;&lt;br /&gt;
* addConnectionLister(connection_listener), removeConnectionLister(connection_listener)&amp;lt;BR&amp;gt; .. for those who want to get notifications&lt;br /&gt;
* bool isConnected()&amp;lt;BR&amp;gt; .. for those who want to poll&lt;br /&gt;
* xxx getServerInfo(), xxx getType()&amp;lt;BR&amp;gt;  .. only valid when isConnected().&lt;br /&gt;
* setUser(), ...&amp;lt;BR&amp;gt;Sets/changes the 'user' that determines the access rights, allowing OPI tools to adjust this per-channel at runtime.&lt;br /&gt;
&lt;br /&gt;
=== Channel, 'Put' Related Methods ===&lt;br /&gt;
* put(new value)&amp;lt;BR&amp;gt;Sends data to the server.&amp;lt;BR&amp;gt;Returns error if currently disconnected or other local problem, no other feedback wether the value actually reached the server.&lt;br /&gt;
* putNotify(new value), putCompletionNotify(new value)&amp;lt;BR&amp;gt;Sends the value to the server, invokes putNotification when receiving record has received the value respectively all the processing triggered by the new value has completed.&lt;br /&gt;
* addPutListener(pnl), removePutListener(pnl)&amp;lt;BR&amp;gt;Need to register listener to actually receive the put*Notify info.&lt;br /&gt;
&lt;br /&gt;
=== Channel, 'Get' Related Methods ===&lt;br /&gt;
* DataAccess = getData(property_list)&amp;lt;BR&amp;gt; Waits for the data.&lt;br /&gt;
* getDataNotify(property_list)&amp;lt;BR&amp;gt;  Will invoke DataListener once data arrives.&lt;br /&gt;
* addDataListener(dn), removeDataListener(dl)&amp;lt;BR&amp;gt;  The data listener is invoked with the channel and the data access interface to the actual data.&amp;lt;br&amp;gt; In addition, does it include event information, so when it's in response to a subscription (see below), we know why the data was sent? Or is that event information embedded in DataAccess?&lt;br /&gt;
&lt;br /&gt;
=== Channel, Subscription Related Methods ===&lt;br /&gt;
* addSubscription(event, filter)&lt;br /&gt;
** event: alarm condition change, some hardware event (like blue beam).&amp;lt;br&amp;gt;Is this still simply a bit mask, and one needs detailed info from the IOC to know that 'blue beam' == bit 14?&lt;br /&gt;
** filters: min/max period, range, rate of change, absolute change&lt;br /&gt;
* cancelSubscription(event, filter)&lt;br /&gt;
&lt;br /&gt;
The idea is that the end result of a subscription is just like a getDataNotify,&lt;br /&gt;
invoking the dataListener, except that a subscription will&lt;br /&gt;
typically return data more than once.&lt;br /&gt;
One can add more then one subscription to the same channel.&lt;br /&gt;
&lt;br /&gt;
=== Container ===&lt;br /&gt;
The Channel does not store any data from a 'put', 'get' or subscription.&lt;br /&gt;
When attaching a container to a channel, the container will subscribe&lt;br /&gt;
to the channel and keep a copy of all data, so one can always ask&lt;br /&gt;
the container for the current values.&lt;br /&gt;
* attach(channel)&lt;br /&gt;
* detach()&lt;br /&gt;
* implements the DataAccess interface to allow access to the data.&lt;br /&gt;
&lt;br /&gt;
== caget 101 ==&lt;br /&gt;
This is how a simple 'caget' could be written:&lt;br /&gt;
&lt;br /&gt;
 Channel channel;&lt;br /&gt;
 channel.connect(&amp;quot;fred&amp;quot;); &lt;br /&gt;
 DataAccess da = channel.get(&amp;quot;value&amp;quot;);&lt;br /&gt;
 cout &amp;lt;&amp;lt; &amp;quot;Value of &amp;quot; &amp;lt;&amp;lt; channel.getName() &amp;lt;&amp;lt; &amp;quot; : &amp;quot; &amp;lt;&amp;lt; da.getAsString(&amp;quot;value&amp;quot;) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;&lt;br /&gt;
 channel.disconnect();&lt;/div&gt;</summary>
		<author><name>KayKasemir</name></author>
	</entry>
</feed>