private PVWriter<T> create(boolean syncWrite) { checkDataSourceAndThreadSwitch(); // Create PVReader and connect PVWriterImpl<T> pvWriter = new PVWriterImpl<T>(syncWrite, Executors.localThread() == notificationExecutor); for (PVWriterListener<T> pVWriterListener : writeListeners) { pvWriter.addPVWriterListener(pVWriterListener); } WriteFunction<T> writeFunction = writeExpression.getWriteFunction(); // TODO: we are ignoring the exception handler for now if (timeoutMessage == null) timeoutMessage = "Write timeout"; PVWriterDirector<T> writerDirector = new PVWriterDirector<T>( pvWriter, writeFunction, dataSource, PVManager.getAsyncWriteExecutor(), notificationExecutor, PVManager.getReadScannerExecutorService(), timeout, timeoutMessage); writerDirector.connectExpression(writeExpression); writerDirector.startScan(TimeDuration.ofMillis(100)); pvWriter.setWriteDirector(writerDirector); return pvWriter; }
/** * Entry point for the library, manages the defaults and allows to create {@link PVReader}, {@link * PVWriter} and {@link PV } from an read or write expression. * * <p><b>NotificationExecutor</b> - This is used for all notifications. By default this uses {@link * org.epics.pvmanager.util.Executors#localThread()} so that the notification are done on whatever * current thread needs to notify. This means that new read notifications are run on threads managed * by the ReadScannerExecutorService, write notifications are run on threads managed by the * DataSource and exceptions notification are run on the thread where the exception is done. This * can be changed to make all notifications routed to single threaded sub-systems, such as UI * environments like SWING, SWT or similar. This can be changed on a PV by PV basis. * * <p><b>AsynchWriteExecutor</b> - This is used for asynchronous writes, to return right away, and * for running timeouts on each write. By default this uses the internal PVManager work pool. The * work submitted here is the calculation of the corresponding {@link WriteExpression} and * submission to the {@link DataSource}. The DataSource itself typically has asynchronous work, * which is executed in the DataSource specific threads. Changing this to {@link * org.epics.pvmanager.util.Executors#localThread()} will make that preparation task on the thread * that calls {@link PVWriter#write(java.lang.Object) } but it will not transform the call in a * synchronous call. * * <p><b>ReadScannerExecutorService</b> - This is used to run the periodic scan for new values. By * default this uses the internal PVManager work pool. The work submitted here is the calculation of * the corresponding {@link DesiredRateExpression} and submission to the NotificationExecutor. * * @author carcassi */ public class PVManager { private static volatile Executor defaultNotificationExecutor = org.epics.pvmanager.util.Executors.localThread(); private static volatile DataSource defaultDataSource = null; private static final ScheduledExecutorService workerPool = Executors.newScheduledThreadPool( Math.max(1, Runtime.getRuntime().availableProcessors() - 1), org.epics.pvmanager.util.Executors.namedPool("PVMgr Worker ")); private static ScheduledExecutorService readScannerExecutorService = workerPool; private static ScheduledExecutorService asyncWriteExecutor = workerPool; /** * Changes the default executor on which all notifications are going to be posted. * * @param notificationExecutor the new notification executor */ public static void setDefaultNotificationExecutor(Executor notificationExecutor) { defaultNotificationExecutor = notificationExecutor; } /** * Returns the current default executor that will execute all notifications. * * @return the default executor */ public static Executor getDefaultNotificationExecutor() { return defaultNotificationExecutor; } /** * Changes the default source for data. * * @param dataSource the data source */ public static void setDefaultDataSource(DataSource dataSource) { PVManager.defaultDataSource = dataSource; } /** * Returns the current default data source. * * @return a data source or null if it was not set */ public static DataSource getDefaultDataSource() { return defaultDataSource; } /** * Reads the given expression, and returns an object to configure the parameters for the read. At * each notification it will return the latest value, even if more had been received from the last * notification. * * @param <T> type of the read payload * @param pvExpression the expression to read * @return the read configuration */ public static <T> PVReaderConfiguration<T> read(SourceRateExpression<T> pvExpression) { return new PVReaderConfiguration<T>(ExpressionLanguage.latestValueOf(pvExpression)); } /** * Reads the given expression, and returns an object to configure the parameters for the read. * * @param <T> type of the read payload * @param pvExpression the expression to read * @return the read configuration */ public static <T> PVReaderConfiguration<T> read(DesiredRateExpression<T> pvExpression) { return new PVReaderConfiguration<T>(pvExpression); } /** * Writes the given expression, and returns an object to configure the parameters for the write. * * @param <T> type of the write payload * @param writeExpression the expression to write * @return the write configuration */ public static <T> PVWriterConfiguration<T> write(WriteExpression<T> writeExpression) { return new PVWriterConfiguration<T>(writeExpression); } /** * Both reads and writes the given expression, and returns an object to configure the parameters * for the both read and write. It's similar to use both {@link * #read(org.epics.pvmanager.expression.SourceRateExpression) } and {@link * #write(org.epics.pvmanager.expression.WriteExpression) } at the same time. * * @param <R> type of the read payload * @param <W> type of the write payload * @param readWriteExpression the expression to read and write * @return the read and write configuration */ public static <R, W> PVConfiguration<R, W> readAndWrite( SourceRateReadWriteExpression<R, W> readWriteExpression) { return readAndWrite(ExpressionLanguage.latestValueOf(readWriteExpression)); } /** * Both reads and writes the given expression, and returns an object to configure the parameters * for the both read and write. It's similar to use both {@link * #read(org.epics.pvmanager.expression.SourceRateExpression) } and {@link * #write(org.epics.pvmanager.expression.WriteExpression) } at the same time. * * @param <R> type of the read payload * @param <W> type of the write payload * @param readWriteExpression the expression to read and write * @return the read and write configuration */ public static <R, W> PVConfiguration<R, W> readAndWrite( DesiredRateReadWriteExpression<R, W> readWriteExpression) { return new PVConfiguration<R, W>(readWriteExpression); } /** * Returns the current executor on which the asynchronous calls are executed. * * @return the current executor */ public static ScheduledExecutorService getAsyncWriteExecutor() { return asyncWriteExecutor; } /** * Changes the executor used for the asynchronous write calls. * * @param asyncWriteExecutor the new executor */ public static void setAsyncWriteExecutor(ScheduledExecutorService asyncWriteExecutor) { PVManager.asyncWriteExecutor = asyncWriteExecutor; } /** * Returns the executor service used to schedule and run the periodic reading scan for new values. * * @return the service for the read operations */ public static ScheduledExecutorService getReadScannerExecutorService() { return readScannerExecutorService; } /** * Changes the executor service to use for executing the periodic read scan. * * @param readScannerExecutorService the new service for the read operations */ public static void setReadScannerExecutorService( ScheduledExecutorService readScannerExecutorService) { PVManager.readScannerExecutorService = readScannerExecutorService; } }