From da06b22aeb03b72eec2de9ec08eefd8c5841c975 Mon Sep 17 00:00:00 2001 From: kasemir Date: Mon, 4 Aug 2025 14:51:56 -0400 Subject: [PATCH 1/4] PVA Server: Provide ClientInfo --- .../java/org/epics/pva/server/PVAServer.java | 21 +++++++++++++++++++ .../java/org/epics/pva/server/ServerAuth.java | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/core/pva/src/main/java/org/epics/pva/server/PVAServer.java b/core/pva/src/main/java/org/epics/pva/server/PVAServer.java index 0b4c847b2b..300e1368a8 100644 --- a/core/pva/src/main/java/org/epics/pva/server/PVAServer.java +++ b/core/pva/src/main/java/org/epics/pva/server/PVAServer.java @@ -10,6 +10,7 @@ import static org.epics.pva.PVASettings.logger; import java.net.InetSocketAddress; +import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap.KeySetView; import java.util.concurrent.ForkJoinPool; @@ -161,6 +162,26 @@ ServerPV getPV(final int sid) return pv_by_sid.get(sid); } + + /** Info about a client to the PVA server: + * Network address and authentication info + */ + public static record ClientInfo(InetSocketAddress address, + ServerAuth authentication) + { + } + + /** Get information about clients to this PVA server + * @return {@link ClientInfo}s + */ + public Collection getClientInfos() + { + return tcp_handlers.stream() + .map(tcp -> new ClientInfo(tcp.getRemoteAddress(), + tcp.getAuth())) + .toList(); + } + /** Special address used in TCP search reply to indicate "Use this TCP connection" */ private static final InetSocketAddress USE_THIS_TCP_CONNECTION = new InetSocketAddress(0); diff --git a/core/pva/src/main/java/org/epics/pva/server/ServerAuth.java b/core/pva/src/main/java/org/epics/pva/server/ServerAuth.java index 1f96b82026..3a42265f7c 100644 --- a/core/pva/src/main/java/org/epics/pva/server/ServerAuth.java +++ b/core/pva/src/main/java/org/epics/pva/server/ServerAuth.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019-2023 Oak Ridge National Laboratory. + * Copyright (c) 2019-2025 Oak Ridge National Laboratory. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -20,7 +20,7 @@ * @author Kay Kasemir */ @SuppressWarnings("nls") -abstract class ServerAuth +public abstract class ServerAuth { /** @param channel Channel for which to check write access * @return Does client have write access? @@ -62,7 +62,7 @@ public static ServerAuth decode(final ServerTCPHandler tcp, final ByteBuffer buf if (PVAAuth.X509.equals(auth)) return new X509ServerAuth(tls_info); - + return Anonymous; } From a5aeb144b32f86d176cb8581772a41f290544358 Mon Sep 17 00:00:00 2001 From: kasemir Date: Mon, 4 Aug 2025 14:52:13 -0400 Subject: [PATCH 2/4] PVATable: Allow access to key element names --- core/pva/src/main/java/org/epics/pva/data/nt/PVATable.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/pva/src/main/java/org/epics/pva/data/nt/PVATable.java b/core/pva/src/main/java/org/epics/pva/data/nt/PVATable.java index f125675ea5..620b885764 100644 --- a/core/pva/src/main/java/org/epics/pva/data/nt/PVATable.java +++ b/core/pva/src/main/java/org/epics/pva/data/nt/PVATable.java @@ -48,9 +48,9 @@ */ public class PVATable extends PVAStructure { public static final String STRUCT_NAME = "epics:nt/NTTable:1.0"; - private static final String LABELS_NAME = "labels"; - private static final String VALUE_NAME = "value"; - private static final String DESCRIPTOR_NAME = "descriptor"; + public static final String LABELS_NAME = "labels"; + public static final String VALUE_NAME = "value"; + public static final String DESCRIPTOR_NAME = "descriptor"; private final PVAStringArray labels; private final PVAStructure value; From 51eadd4fb58a3545550028124501821c08791543 Mon Sep 17 00:00:00 2001 From: kasemir Date: Mon, 4 Aug 2025 15:33:24 -0400 Subject: [PATCH 3/4] pvaclient cmd line 'call' option --- .../org/epics/pva/client/PVAClientMain.java | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/core/pva/src/main/java/org/epics/pva/client/PVAClientMain.java b/core/pva/src/main/java/org/epics/pva/client/PVAClientMain.java index f2a5fab4bc..a6f4ea1803 100644 --- a/core/pva/src/main/java/org/epics/pva/client/PVAClientMain.java +++ b/core/pva/src/main/java/org/epics/pva/client/PVAClientMain.java @@ -20,6 +20,7 @@ import org.epics.pva.PVASettings; import org.epics.pva.data.PVAData; +import org.epics.pva.data.PVAStructure; /** Command line tool for PVA client * @@ -39,7 +40,7 @@ public class PVAClientMain private static void help() { - System.out.println("USAGE: pvaclient info|get|monitor|put|beacons [options] ..."); + System.out.println("USAGE: pvaclient info|get|monitor|put|call|beacons [options] ..."); System.out.println(); System.out.println("Options:"); System.out.println(" -h Help"); @@ -54,6 +55,7 @@ private static void help() System.out.println("get Read PV's value"); System.out.println("monitor Subscribe to PV's value changes"); System.out.println("put Write value to PV"); + System.out.println("call Call RPC PV (no request params)"); System.out.println("beacons Display received beacons"); } @@ -240,6 +242,36 @@ private static void put(final String name, final String value) throws Exception } } + private static void call(final String name, final PVAStructure request) throws Exception + { + try (final PVAClient pva = new PVAClient()) + { + final CountDownLatch connected = new CountDownLatch(1); + final PVAChannel pv = pva.getChannel(name, (ch, state) -> + { + if (state == ClientChannelState.CONNECTED) + connected.countDown(); + }); + final long timeout_ms = Math.round(seconds*1000); + if (! connected.await(timeout_ms, TimeUnit.MILLISECONDS)) + { + System.err.println("Timeout waiting for " + name); + return; + } + + try + { + PVAStructure result = pv.invoke(request).get(timeout_ms, TimeUnit.MILLISECONDS); + System.out.println(result); + } + catch (TimeoutException ex) + { + System.err.println("Call timed out"); + } + pv.close(); + } + } + /** Watch received beacons * @throws Exception on error */ @@ -351,6 +383,12 @@ else if (command.equals("put") && names.size() == 2) request = "value"; put(names.get(0), names.get(1)); } + else if (command.equals("call") && names.size() > 0) + { + // For now not supporting any request detail from cmdline + PVAStructure request = new PVAStructure("", ""); + call(names.get(0), request); + } else help(); } From d30d7b07696fa121074183bb789e4040663908c7 Mon Sep 17 00:00:00 2001 From: kasemir Date: Wed, 6 Aug 2025 08:25:04 -0400 Subject: [PATCH 4/4] Turn commands into enum --- .../org/epics/pva/client/PVAClientMain.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/core/pva/src/main/java/org/epics/pva/client/PVAClientMain.java b/core/pva/src/main/java/org/epics/pva/client/PVAClientMain.java index a6f4ea1803..cebbede602 100644 --- a/core/pva/src/main/java/org/epics/pva/client/PVAClientMain.java +++ b/core/pva/src/main/java/org/epics/pva/client/PVAClientMain.java @@ -8,6 +8,7 @@ package org.epics.pva.client; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -17,6 +18,7 @@ import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; +import java.util.stream.Collectors; import org.epics.pva.PVASettings; import org.epics.pva.data.PVAData; @@ -38,9 +40,17 @@ public class PVAClientMain private static boolean completion = false; private static String request = ""; + /** Commands */ + private static enum Cmd + { + info, get, monitor, put, call, beacons + } + private static void help() { - System.out.println("USAGE: pvaclient info|get|monitor|put|call|beacons [options] ..."); + System.out.println("USAGE: pvaclient " + + Arrays.stream(Cmd.values()).map(Object::toString).collect(Collectors.joining("|")) + + " [options] ..."); System.out.println(); System.out.println("Options:"); System.out.println(" -h Help"); @@ -368,22 +378,22 @@ else if (arg.startsWith("-")) final String command = names.remove(0); - if (command.equals("beacons") && names.size() == 0) + if (command.equals(Cmd.beacons.name()) && names.size() == 0) beacons(); - else if (command.equals("info") && names.size() > 0) + else if (command.equals(Cmd.info.name()) && names.size() > 0) info(names); - else if (command.equals("get") && names.size() > 0) + else if (command.equals(Cmd.get.name()) && names.size() > 0) get(names); - else if (command.equals("monitor") && names.size() > 0) + else if (command.equals(Cmd.monitor.name()) && names.size() > 0) monitor(names); - else if (command.equals("put") && names.size() == 2) + else if (command.equals(Cmd.put.name()) && names.size() == 2) { // By default, write to the 'value' element data structure if (request.isEmpty()) request = "value"; put(names.get(0), names.get(1)); } - else if (command.equals("call") && names.size() > 0) + else if (command.equals(Cmd.call.name()) && names.size() > 0) { // For now not supporting any request detail from cmdline PVAStructure request = new PVAStructure("", "");