55package org .epics .gpclient ;
66
77import java .time .Duration ;
8+ import java .util .concurrent .CountDownLatch ;
9+ import java .util .concurrent .ExecutionException ;
810import java .util .concurrent .Executor ;
11+ import java .util .concurrent .Future ;
912import java .util .concurrent .ScheduledExecutorService ;
13+ import java .util .concurrent .TimeUnit ;
14+ import java .util .concurrent .TimeoutException ;
15+ import java .util .concurrent .atomic .AtomicReference ;
16+ import java .util .function .Consumer ;
1017import org .epics .gpclient .datasource .DataSource ;
1118import org .epics .vtype .VType ;
1219import static org .epics .gpclient .GPClient .*;
@@ -31,6 +38,85 @@ public class GPClientInstance {
3138 this .defaultNotificationExecutor = config .defaultNotificationExecutor ;
3239 }
3340
41+ /**
42+ * Reads the value of the given expression, asking for {@link VType} values.
43+ *
44+ * @param channelName the name of the channel
45+ * @return the future value
46+ */
47+ public Future <VType > readOnce (String channelName ) {
48+ return readOnce (channel (channelName ));
49+ }
50+
51+ /**
52+ * Reads the value of the given expression.
53+ *
54+ * @param <R> the read type
55+ * @param expression the expression to read
56+ * @return the future value
57+ */
58+ public <R > Future <R > readOnce (Expression <R , ?> expression ) {
59+ final AtomicReference <R > result = new AtomicReference <>();
60+ final AtomicReference <Exception > error = new AtomicReference <>();
61+ final CountDownLatch latch = new CountDownLatch (1 );
62+ final PVReader <R > pvReader = read (expression ).addReadListener ((event , pv ) -> {
63+ if (event .isType (PVEvent .Type .VALUE )) {
64+ result .set (pv .getValue ());
65+ pv .close ();
66+ latch .countDown ();
67+ }
68+ if (event .isType (PVEvent .Type .EXCEPTION )) {
69+ error .set (event .getException ());
70+ pv .close ();
71+ latch .countDown ();
72+ }
73+ }).start ();
74+ Future <R > future = new Future <R >() {
75+ @ Override
76+ public boolean cancel (boolean mayInterruptIfRunning ) {
77+ boolean toCancel = !pvReader .isClosed ();
78+ if (toCancel ) {
79+ pvReader .close ();
80+ }
81+ return toCancel ;
82+ }
83+
84+ @ Override
85+ public boolean isCancelled () {
86+ return pvReader .isClosed () && result .get () == null ;
87+ }
88+
89+ @ Override
90+ public boolean isDone () {
91+ return pvReader .isClosed () && result .get () != null ;
92+ }
93+
94+ @ Override
95+ public R get () throws InterruptedException , ExecutionException {
96+ latch .await ();
97+ return getResult ();
98+ }
99+
100+ private R getResult () throws ExecutionException {
101+ if (error .get () != null ) {
102+ throw new ExecutionException (error .get ());
103+ } else {
104+ return result .get ();
105+ }
106+ }
107+
108+ @ Override
109+ public R get (long timeout , TimeUnit unit ) throws InterruptedException , ExecutionException , TimeoutException {
110+ if (latch .await (timeout , unit )) {
111+ return getResult ();
112+ } else {
113+ throw new TimeoutException ();
114+ }
115+ }
116+ };
117+ return future ;
118+ }
119+
34120 /**
35121 * Reads the channel with the given name, asking for {@link VType} values.
36122 *
0 commit comments