Package org.epics.pvmanager

pvManager

See:
          Description

Interface Summary
ChannelWriteCallback Called by the ChannelHandler once a write is completed.
DataSourceTypeAdapter<ConnectionPayload,MessagePayload> Matches and fills a cache with the data from connection and message payloads.
DataSourceTypeAdapterSet A set of type adapters.
ExpressionLanguage.OneArgFunction<R,A> A user provided single argument function.
ExpressionLanguage.TwoArgFunction<R,A1,A2> A user provided double argument function.
PVReader<T> An object representing the PVReader.
PVReaderListener Callback for any change in the PV value.
PVWriter<T> An object representing a writable PV.
PVWriterListener Callback for delivery notification of new value.
 

Class Summary
Aggregator<R,A> Aggregates the data out of a Collector into a new data type.
BasicTypeSupport Implements support for basic standard java types.
ChannelHandler Manages the connection for each channel of a data source.
ChannelHandlerReadSubscription Groups all the parameters required to add a reader to a ChannelHandler.
ChannelRecipe  
Collector<T> Collects the data at the CA rate and allows a client to get all values since last check.
CompositeDataSource A data source that can dispatch a request to multiple different data sources.
DataRecipe Represents all the information necessary to connect to a DataSource.
DataRecipeBuilder Builder class for DataRecipe.
DataSource A source for data that is going to be processed by the PVManager.
DataSourceTypeSupport The type support for a datasource.
ExceptionHandler This class receives all the exceptions generated by a PV.
ExpressionLanguage Operators to constructs expression of PVs that the PVManager will be able to monitor.
ExpressionLanguage.Filter<T> Filters a data stream, removing updates that match the given function.
Function<R> A basic building block in the PVManager framework that can return a result of a given type.
MultiplexedChannelHandler<ConnectionPayload,MessagePayload> Implements a ChannelHandler on top of a single subscription and multiplexes all reads on top of it.
Notification<T> Used by NotificationSupport to communicate whether a new notification is needed, and what should be the type to be notified.
NotificationSupport<T> Dedicated notification type support.
PV<R,W> A PV that can be both read and written.
PVConfiguration<R,W> Allows to configure the type of read/write PV to create.
PVManager Entry point for the library, manages the defaults and allows to create PVReader, PVWriter and PV from an read or write expression.
PVReaderConfiguration<T> An expression used to set the final parameters on how the pv expression should be monitored.
PVWriterConfiguration<T> An expression used to set the final parameters on how the pv expression should be written.
TimeSupport<T> Deprecated. generic type support is being removed: was too intrusive and little used.
TypeSupport<T> Implements the mechanism for registering different types so that the library knows how to handle them.
ValueCache<T> Represent a building block that can store a particular value
WriteBuffer Represents all the values, channel names and ordering information needed for writing
WriteBufferBuilder A builder for WriteBuffer.
WriteCache<T> Represent part of the write buffer that holds the value for one pv.
WriteFunction<A> A basic building block in the PVManager framework that can write an object of a given type.
 

Exception Summary
ReadFailException Exception thrown when a PVReader is trying to read a channel or data source that cannot be read from.
TimeoutException Exception thrown when a PVReader or PVWriter exceed their timeout.
WriteFailException Exception thrown when a PVWriter is trying to write to a read-only channel or data source.
 

Package org.epics.pvmanager Description

pvManager

Contents

Configuration

  1. Using PVManager in CSS
  2. Using PVManager in Swing
  3. Configuring JCA/CAJ as the default data source
  4. Configuring multiple data sources with different prefixes

Basic usage

  1. Reading a single channel
  2. Reading all values values from a channel
  3. Writing a single channel asynchrnously
  4. Writing a single channel synchrnously
  5. Reading and writing a single channel
  6. Handling read errors on notifications
  7. Handling read errors using an ExceptionHandler
  8. Setting read connection timeouts

Multiple channels

  1. Reading a map with multiple channels
  2. Writing a map with multiple channels
  3. Read and write a map with multiple channels
  4. Refer to channel with a different name
  5. Impose write ordering

Working with standard VTypes

  1. Read/Write a specific type
  2. Working with an unknown type: extracting alarm, time, ...
  3. Working with an unknown type: switch on the type
  4. Working with an unknown type: register listener on type

Working with VTable

  1. Assembling a table

Using PVManager in CSS

In CSS, data sources are configured by adding the appropriate plug-ins, so you must not change the default configuration. If you are developing user interfaces in SWT, you will want to route the notifications on the SWT thread.
 // Import from here
 import static org.csstudio.utility.pvmanager.ui.SWTUtil.*;
 
 // When creating a pv, remember to ask for notification on the SWT thread
 PVReader<?> pvReader = PVManager.read(...)..notifyOn(swtThread()).maxRate(ofMillis(100));
 

Using PVManager in Swing

You will first need to configure the data sources yourself (see other examples). You will want to route notification directly on the Event Dispatch Thread. You can do this on a PV by PV basis, or you can change the default.
 // Import from here
 import static org.epics.pvmanager.util.Executors.*;
 
 // Route notification for this pv on the Swing EDT
 PVReader<?> pvReader = PVManager.read(...).notifyOn(swingEDT()).maxRate(ofMillis(100));
 
 // Or you can change the default
 PVManager.setDefaultNotificationExecutor(swingEDT());
 

Configuring JCA/CAJ as the default data source

 // Sets CAJ (pure java implementation) as the default data source,
 // monitoring both value and alarm changes
 PVManager.setDefaultDataSource(new JCADataSource());
 
 // For utltimate control, you can create the JCA context yourself
 // and pass it to the data source
 ...
 Context jcaContext = ...
 PVManager.setDefaultDataSource(new JCADataSource(jcaContext, Monitor.VALUE | Monitor.ALARM));
 
For more options, check the constructors for JCADataSource.

Configuring multiple data sources with different prefixes

 // Create a multiple data source, and add different data sources
 CompositeDataSource composite = new CompositeDataSource();
 composite.putDataSource("ca", new JCADataSource());
 composite.putDataSource("sim", new SimulationDataSource());
 
 // If no prefix is given to a channel, use JCA as default
 composite.setDefaultDataSource("ca");
 
 // Set the composite as the default
 PVManager.setDefaultDataSource(composite);
 
For more options, check the documentation for CompositeDataSource.

Reading a single channel

 // Let's statically import so the code looks cleaner
 import static org.epics.pvmanager.ExpressionLanguage.*;
 import static org.epics.util.time.TimeDuration.*;
 
 // Read channel "channelName" up to every 100 ms
 final PVReader<Object> pvReader = PVManager.read(channel("channelName")).maxRate(ofMillis(100));
 pvReader.addPVReaderListener(new PVReaderListener() {
     public void pvChanged() {
         // Do something with each value
         Object newValue = pvReader.getValue();
         System.out.println(newValue);
     }
 });
 
 // Remember to close
 pvReader.close();
 
The interval between updates can be specified in different units (e.g. ms, sec, min, hour, hz). Check the documentation at TimeDuration.

Reading all values values from a channel

 // Read channel "channelName" up to every 100 ms, and get all
 // the new values from the last notification.
 final PVReader<List<Object>> pvReader = PVManager.read(newValuesOf(channel("channelName"))).maxRate(ofMillis(100));
 pvReader.addPVReaderListener(new PVReaderListener() {
     public void pvChanged() {
         // Do something with each value
         for (Object newValue : pvReader.getValue()) {
             System.out.println(newValue);
         }
     }
 });
 
 // Remember to close
 pvReader.close();
 
To limit memory consumption, you can specify the maximum amount of values to retain. See all options at ExpressionLanguage.

Writing a single channel asynchronously

 PVWriter<Object> pvWriter = PVManager.write(channel("channelName")).async();
 pvWriter.addPVWriterListener(new PVWriterListener() {
     public void pvWritten() {
         System.out.println("Write finished");
     }
 });
 // This will return right away, and the notification will be sent
 // on the listener
 pvWriter.write("New value");
 
 // Remember to close
 pvWriter.close();
 

Writing a single channel synchronously

 PVWriter<Object> pvWriter = PVManager.write(channel("channelName")).sync();
 // This will block until the write is done
 pvWriter.write("New value");
 System.out.println("Write finished");
 
 // Remember to close
 pvWriter.close();
 

Reading and writing a single channel

 // A PV is both a PVReader and a PVWriter
 final PV<Object, Object> pv = PVManager.readAndWrite(channel("channelName")).asynchWriteAndMaxReadRate(ofMillis(10));
 pv.addPVReaderListener(new PVReaderListener() {
     public void pvChanged() {
         // Do something with each value
         Object newValue = pv.getValue();
         System.out.println(newValue);
     }
 });
 pv.write("New value");
 
 // Remember to close
 pv.close();
 

Handling read errors on notifications

 final PVReader<Object> pvReader = PVManager.read(channel("channelName")).maxRate(ofMillis(100));
 pvReader.addPVReaderListener(new PVReaderListener() {
 
     public void pvChanged() {
         // By default, read exceptions are made available
         // on the reader itself.
         // This will give you only the last exception, so if
         // more then one exception was generated after the last read,
         // some will be lost.
         Exception ex = pvReader.lastException();
         
         // Note that taking the exception, clears it
         // so next call you'll get null.
         if (pvReader.lastException() == null) {
             // Always true
         }
     }
 });
 

Handling read errors using an ExceptionHandler

 // All read exceptions will be passed to the exception handler
 // on the thread that it generates them. The handler, therefore,
 // must be thread safe. Overriding the exception handling means
 // disabling the default handling, so read exception will no longer
 // be accessible with pvReader.lastException()
 final PVReader<Object> pvReader = PVManager.read(channel("channelName"))
         .routeExceptionsTo(new ExceptionHandler() {
             public void handleException(Exception ex) {
                 System.out.println("Error: " + ex.getMessage());
             }
         }).maxRate(ofMillis(100));
 

Setting read connection timeouts

 // If after 5 seconds no new value comes (i.e. pvReader.getValue() == null)
 // then a timeout is sent. PVManager will _still_ try to connect,
 // until pvReader.close() is called.
 // The timeout will be notified only on the first connection.
 final PVReader<Object> pvReader = PVManager.read(channel("channelName")).timeout(sec(5)).maxRate(ofMillis(100));
 pvReader.addPVReaderListener(new PVReaderListener() {
 
     public void pvChanged() {
         // Timeout are passed as exceptions. This allows you to
         // treat them as any other error conditions.
         Exception ex = pvReader.lastException();
         if (ex instanceof TimeoutException) {
             System.out.println("Didn't connected after 5 seconds");
         }
     }
 });
 

Reading a map with multiple channels

 // Read a map with the channels named "one", "two" and "three"
 final PVReader<Map<String, Object>> pvReader = PVManager.read(mapOf(latestValueOf(channels("one", "two", "three")))).maxRate(ofMillis(100));
 pvReader.addPVReaderListener(new PVReaderListener() {
     public void pvChanged() {
         // Print the values if any
         Map<String, Object> map = pvReader.getValue();
         if (map != null) {
             System.out.println("one: " + map.get("one") +
                     " - two: " + map.get("two") + 
                     " - three: " + map.get("three"));
         }
     }
 });
  
 // Remember to close
 pvReader.close();
 
Note that when using a composite datasource, the channels can be from different sources (e.g. "sim://noise" and "ca://mypv").

Writing a map with multiple channels

 // Write a map to the channels named "one", "two" and "three"
 PVWriter<Map<String, Object>> pvWriter = PVManager.write(mapOf(channels("one", "two", "three"))).async();
 
 // Prepare the 3 values
 Map<String, Object> values = new HashMap<String, Object>();
 values.put("one", 1.0);
 values.put("two", 2.0);
 values.put("three", "run");
 
 // Write
 pvWriter.write(values);
 
 // Remember to close
 pvWriter.close();
 
Note that when using a composite datasource, the channels can be from different sources (e.g. "sim://noise" and "ca://mypv").

Read and write a map with multiple channels

 // Read and write a map to the channels named "one", "two" and "three"
 PV<Map<String, Object>, Map<String, Object>> pv = PVManager.readAndWrite(
         mapOf(latestValueOf(channels("one", "two", "three")))).asynchWriteAndMaxReadRate(ofMillis(100));
 
 // Do something
 // ...
 
 // Remember to close
 pv.close();
 

Refer to channel with a different name

 // Read a map with the channels "one", "two" and "three"
 // reffered in the map as "setpoint", "readback" and "difference"
 final PVReader<Map<String, Object>> pvReader = PVManager.read(mapOf(
         latestValueOf(channel("one").as("setpoint").and(channel("two").as("readback")).and(channel("three").as("difference"))))).maxRate(ofMillis(100));
 pvReader.addPVReaderListener(new PVReaderListener() {
 
     public void pvChanged() {
         // Print the values if any
         Map<String, Object> map = pvReader.getValue();
         if (map != null) {
             System.out.println("setpoint: " + map.get("setpoint") +
                     " - readback: " + map.get("readback") + 
                     " - difference: " + map.get("difference"));
         }
     }
 });
 
 // Remember to close
 pvReader.close();
 
You can rename channels and any read expression, regardless of how they are combined later.

Impose write ordering

 // Write a map to the channels named "one", "two" and "three"
 // Write "two" after "one" and write "three" after "two"
 PVWriter<Map<String, Object>> pvWriter = PVManager.write(
         mapOf(channel("one")
               .and(channel("two").after("one"))
               .and(channel("three").after("two")))).async();
 
 // Do something
 // ...
 
 // Remember to close
 pvWriter.close();
 
Note that when using a composite datasource, the channels can be from different sources (e.g. "sim://noise" and "ca://mypv"). The write ordering will also be respected across sources.

Read/Write a specific type

 // Let's statically import so the code looks cleaner
 import static org.epics.pvmanager.data.ExpressionLanguage.*;
 
 // Read and Write a vDouble
 // Note that the read type is different form the write type
 final PV<VDouble, Double> pv = PVManager.readAndWrite(vDouble("currentRB")).asynchWriteAndMaxReadRate(ofMillis(100));
 pv.addPVReaderListener(new PVReaderListener() {
 
     public void pvChanged() {
         VDouble value = pv.getValue();
         if (value != null) {
             System.out.println(value.getValue() + " " + value.getAlarmSeverity());
         }
     }
 });
 pv.write(1.0);
 
 // Remember to close
 pv.close();
 
For a full list of types, refer to ExpressionLanguage.

Working with an unknown type: extracting alarm, time, ...

 // We connect to a channel that produces a VType, but we
 // don't know which one
 final PVReader<VType> pvReader = PVManager.read(vType("channelName")).maxRate(ofMillis(100));
 pvReader.addPVReaderListener(new PVReaderListener() {
 
     public void pvChanged() {
         VType value = pvReader.getValue();
         // We can extract the different aspect of the read object,
         // so that we can work on them separately
         
         // This returns the interface implemented (VDouble, VInt, ...)
         Class<?> type = ValueUtil.typeOf(value);
         // Extracts the alarm if present
         Alarm alarm = ValueUtil.alarmOf(value);
         // Extracts the time if present
         Time time = ValueUtil.timeOf(value);
         // Extracts a numeric value if present
         Double number = ValueUtil.numericValueOf(value);
         // Extract display information if present
         Display display = ValueUtil.displayOf(value);
         
         setAlarm(alarm);
         // ...
     }
 });
 

Working with an unknown type: switch on the type

 final PVReader<VType> pvReader = PVManager.read(vType("channelName")).maxRate(ofMillis(100));
 pvReader.addPVReaderListener(new PVReaderListener() {
 
     public void pvChanged() {
         // We can switch on the full type
         if (pvReader.getValue() instanceof VDouble) {
             VDouble vDouble = (VDouble) pvReader.getValue();
             // Do something with a VDouble
         }
         // ...
     }
 });
 

Working with an unknown type: register listener on type

 final PVReader<VType> pvReader = PVManager.read(vType("channelName")).maxRate(ofMillis(100));
 pvReader.addPVReaderListener(VDouble.class, new PVReaderListener() {
 
     public void pvChanged() {
         // We are already guaranteed that the cast succeeds
         // and that the value is not null
         VDouble vDouble = (VDouble) pvReader.getValue();
         System.out.println(vDouble.getValue());
         // ...
     }
 });
 

Assembling a table

You can assemble a table by giving a desired rate expression for each cell, organizing them by column. You can use constant expressions for labels or values that do not change.
 List<String> names = Arrays.asList("one", "two", "trhee");
 final PVReader<VTable> pvReader = PVManager.read(vTable(
         column("Names", vStringConstants(names)),
         column("Values", latestValueOf(vType(names)))))
         .maxRate(ofMillis(100));
 pvReader.addPVReaderListener(new PVReaderListener() {
 
     public void pvChanged() {
         VTable vTable = pvReader.getValue();
         // First column is the names
         String[] names = (String[]) vTable.getColumnArray(0);
         // Second column is the values
         double[] values = (double[]) vTable.getColumnArray(1);
         // ...
     }
 });
 

Package description

This package contains all the basic components of the PVManager framework and the basic support for the language to define the creation.

There are two distinct parts in the PVManager framework. The first part includes all the elements that deal with data directly: read from various sources (DataSource), performing computation (Function), collecting data (Collector), scanning at the UI rate (Notifier) and notify on appropriate threads.

The second part consists of an expression language that allows to define how to connect the first set of objects with each other. SourceRateExpression describes data as it's coming out at the network rate, DesiredRateExpression defines data at the scanning rate for the UI, and ExpressionLanguage defines static methods that define the operator in the expression language.

Users can extend both the first part (by extending support for different types, providing different support for different data source or creating new computation elements) and the second part (by extending the language to support other cases. All support for data types is relegated to separate packages: you can use the same style to extend the framework to your needs.