Package org.epics.pvmanager

pvManager

See: Description

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.

Copyright © 2012. All Rights Reserved.