Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for TCP Searches #192

Open
wants to merge 83 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
9f76c2f
add ability to connect to server channels when UDP search is not feas…
sveseli Jan 24, 2024
63548a1
remove un-needed code
sveseli Jan 24, 2024
99bfee2
allow control of the UDP sender port via the EPICS_PVA_UDP_SENDER_POR…
sveseli Jan 25, 2024
a0c436f
add tests for udp search, name server search, and direct connection
sveseli Mar 11, 2024
6e7420a
set connected status on state change
sveseli Mar 11, 2024
4de9a83
add method for retrieving search server port
sveseli Mar 11, 2024
6976fdc
add new tests to Makefile
sveseli Mar 11, 2024
6fe5a79
fix typo
sveseli Mar 11, 2024
8af89e7
add name server search
sveseli Mar 11, 2024
7caa5d3
add protocol constants
sveseli Mar 11, 2024
424090f
use constant for type
sveseli Mar 11, 2024
57c03c8
support for name server search
sveseli Mar 11, 2024
291d97f
use constant for tcp transport
sveseli Mar 11, 2024
03feff9
add interface for getting name server transport
sveseli Mar 11, 2024
f1bdbb2
add support for tcp search
sveseli Mar 11, 2024
c5dc3af
add support for name server channel discovery
sveseli Mar 12, 2024
8b32dba
add tcp acceptor for channel search
sveseli Mar 12, 2024
bcaa794
support for tcp channel search
sveseli Mar 12, 2024
48fb839
introduce new interfaces for managing tcp transport
sveseli Mar 13, 2024
d78b5a1
use different channel priority for name server searches; move name se…
sveseli Mar 13, 2024
98ec0bc
stop search if channel is found via direct connection
sveseli Mar 13, 2024
9425a18
use default address list if provided string is empty
sveseli Mar 15, 2024
bac2944
allow initializing server context with different set of response hand…
sveseli Mar 18, 2024
581f611
expose additional header
sveseli Mar 18, 2024
0cc8c2b
fix logging typo
sveseli Mar 19, 2024
53e15c4
add utility function to convert string to inet address
sveseli Mar 20, 2024
b3cb0d7
move utility functions to pvutils.cpp
sveseli Mar 20, 2024
d8a1dba
add PVA name server utility
sveseli Mar 20, 2024
797aa77
add name server utility description
sveseli Mar 20, 2024
fd115a8
add utilities for manipulating strings
sveseli Mar 21, 2024
9dcd131
add additional utility methods
sveseli Mar 21, 2024
6719d20
add support for a static channel map read from a file
sveseli Mar 21, 2024
3c598c2
update description
sveseli Mar 21, 2024
18a7e2a
send search response immediately
sveseli Mar 21, 2024
f629385
send name server search response immediately
sveseli Mar 21, 2024
cdf914c
merge changes for udp sender port
sveseli Mar 21, 2024
94e9042
update for older gcc compiler
sveseli Mar 22, 2024
db58c6f
fix windows build
sveseli Mar 22, 2024
f63cb89
move base nameserver code to the library
sveseli Mar 25, 2024
249e7f0
fix build on older gcc version
sveseli Mar 25, 2024
56f504a
fixes for mingw builds
sveseli Mar 25, 2024
0e237d9
fix few log formats
sveseli Mar 25, 2024
1007a9c
fix few codacy complaints; avoid duplicate symbold on rtems
sveseli Mar 26, 2024
8d0135a
move string constants to pvaConstants
sveseli Mar 27, 2024
26ff188
include missing header
sveseli Mar 27, 2024
70e52cf
fix few log statement formats
sveseli Mar 28, 2024
270b472
add diagnostics output for channel discovery tests
sveseli Mar 28, 2024
3223142
initialize udp sender port
sveseli Mar 28, 2024
278e24a
add more diagnostics
sveseli Mar 28, 2024
e75b24a
prevent direct connection attempt to unknown port
sveseli Mar 28, 2024
7b4604d
restore logging level
sveseli Mar 28, 2024
c9e23d1
resolve codacy complaints
sveseli Mar 29, 2024
bfbde63
reduce test logging level
sveseli Mar 29, 2024
d14f6d8
fix setsockopt call for mingw
sveseli Mar 29, 2024
b4a9f7a
add missing 2 bytes
sveseli Mar 29, 2024
fc33afb
revert protocol type back to hardcoded string
sveseli Mar 29, 2024
658d57d
use static cast for %lu format
sveseli Mar 29, 2024
10242e8
move stuff around to avoid exposing additional headers
sveseli Mar 30, 2024
c9227f9
few more changes for mingw
sveseli Mar 31, 2024
4527a0b
fix codacy complaint about explicit constructors
sveseli Mar 31, 2024
7832fc4
restore original export defs; remove unused constants
sveseli Mar 31, 2024
6b19c20
fix format warning
sveseli Apr 1, 2024
55bd40e
add test disgnostics output
sveseli Apr 1, 2024
cc5d2d6
new test added
sveseli Apr 1, 2024
1e530ba
remove pvans utility
sveseli Apr 12, 2024
afbd308
remove nameserver support classes
sveseli Apr 12, 2024
b521694
remove string utility tests
sveseli Apr 15, 2024
731bba1
remove ability to set udp sender port
sveseli Apr 15, 2024
0dca5d7
disable code that initiates direct connection to server based on EPIC…
sveseli Apr 15, 2024
899cde9
remove search acceptor; name server searches have to go through the r…
sveseli Apr 15, 2024
487b11e
remove unused method
sveseli Apr 15, 2024
4e8569a
add default implementation of isUsed()
sveseli Apr 15, 2024
046c1f2
add default implementation name server search methods
sveseli Apr 15, 2024
80c708c
remove pvaConstants.cpp, restore pvaVersion.cpp, remove PVA_UDP_SENDE…
sveseli Apr 17, 2024
5f001f5
remove udp sender port variables
sveseli Apr 17, 2024
fd15386
remove unused utility method for converting string to inet address
sveseli May 16, 2024
0179b3c
remove unused methods, restore original initialize() method for serve…
sveseli May 16, 2024
fe0be26
remove unused server search response handler class
sveseli May 16, 2024
39c6106
reduce logging for request start
sveseli May 16, 2024
2383407
force name server transport release if channel is not found after max…
sveseli May 16, 2024
7a64947
remove blocking call for getting name server connections, and instead…
sveseli May 29, 2024
bb5fce6
do not release all name server connections if channel search was unsu…
sveseli May 29, 2024
0a1f8f6
wait for name server transport only if the search has been just sched…
sveseli Jul 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions pvtoolsSrc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ include $(TOP)/configure/CONFIG

USR_CPPFLAGS += -I$(TOP)/src/utils
USR_CPPFLAGS += -I$(TOP)/src/remote
USR_CPPFLAGS += -I$(TOP)/src/remoteClient

PROD_DEFAULT += pvget
pvget_SRCS += pvget.cpp
Expand All @@ -27,6 +28,12 @@ pvinfo_SRCS += pvutils.cpp

PROD_DEFAULT += pvlist
pvlist_SRCS += pvlist.cpp
pvlist_SRCS += pvutils.cpp

PROD_DEFAULT += pvans
pvans_SRCS += pvans.cpp
pvans_SRCS += pvutils.cpp
pvans_SRCS += nameServerImpl.cpp

PROD_LIBS += pvAccessCA pvAccess pvData ca Com

Expand Down
91 changes: 91 additions & 0 deletions pvtoolsSrc/nameServerImpl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvAccessCPP is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/


#include <pv/hexDump.h>
#include <pv/stringUtility.h>

#include "nameServerImpl.h"
#include "pvutils.h"

using namespace std;
using namespace epics::pvData;
using epics::pvAccess::ChannelDiscovery::ChannelEntry;
using epics::pvAccess::ChannelDiscovery::ChannelMap;
using epics::pvAccess::ChannelDiscovery::ServerAddressList;


namespace epics { namespace pvAccess {


NameServerImpl::NameServerImpl(const epics::pvAccess::Configuration::shared_pointer& conf)
: NameServer(conf)
{
}

NameServerImpl::~NameServerImpl()
{
}

void NameServerImpl::discoverServers(ServerAddressList& serverAddressList)
{
if (!autoDiscovery) {
LOG(logLevelDebug, "Skipping server discovery");
return;
}

LOG(logLevelDebug, "Starting server discovery");
std::string nsGuid = ::toHex(nameServerGuid.value, sizeof(nameServerGuid.value));
ServerMap serverMap;
::discoverServers(timeout, serverMap);

int nDiscoveredServers = 0;
for (ServerMap::const_iterator it = serverMap.begin(); it != serverMap.end(); ++it) {
const ServerEntry& entry = it->second;
if (nsGuid == entry.guid) {
LOG(logLevelDebug, "Ignoring our own server GUID 0x%s", entry.guid.c_str());
continue;
}
size_t count = entry.addresses.size();
std::string addresses = " ";
for (size_t i = 0; i < count; i++) {
addresses = addresses + inetAddressToString(entry.addresses[i]) + " ";
}
LOG(logLevelDebug, "Found server GUID 0x%s version %d: %s@[%s]", entry.guid.c_str(), (int)entry.version, entry.protocol.c_str(), addresses.c_str());
if (count > 0) {
std::string serverAddress = inetAddressToString(entry.addresses[0]);
if (addUniqueServerToList(serverAddress, serverAddressList)) {
nDiscoveredServers++;
}
}
}
LOG(logLevelDebug, "Discovered %d servers", nDiscoveredServers);
}

void NameServerImpl::discoverServerChannels(const std::string& serverAddress, ChannelMap& channelMap)
{
LOG(logLevelDebug, "Discovering channels for server %s", serverAddress.c_str());
try {
PVStructure::shared_pointer ret = getChannelInfo(serverAddress, "channels", timeout);
PVStringArray::shared_pointer pvs(ret->getSubField<PVStringArray>("value"));
PVStringArray::const_svector val(pvs->view());
epicsTimeStamp now;
epicsTimeGetCurrent(&now);
for (unsigned int i = 0; i < val.size(); i++) {
ChannelEntry channelEntry;
channelEntry.channelName = val[i];
channelEntry.serverAddress = serverAddress;
channelEntry.updateTime = now;
channelMap[val[i]] = channelEntry;
}
LOG(logLevelDebug, "Discovered %d channels for server %s", int(val.size()), serverAddress.c_str());
}
catch(std::exception& e) {
LOG(logLevelError, "Error retrieving channels for server %s: %s", serverAddress.c_str(), e.what());
}
}

}}
23 changes: 23 additions & 0 deletions pvtoolsSrc/nameServerImpl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef NAME_SERVER_IMPL_H
#define NAME_SERVER_IMPL_H

#include <pv/nameServer.h>

namespace epics { namespace pvAccess {

class NameServerImpl
: public NameServer
, public std::tr1::enable_shared_from_this<NameServerImpl>
{
public:
POINTER_DEFINITIONS(NameServerImpl);
NameServerImpl(const epics::pvAccess::Configuration::shared_pointer& conf);
virtual ~NameServerImpl();

virtual void discoverServers(ChannelDiscovery::ServerAddressList& serverAddressList);
virtual void discoverServerChannels(const std::string& serverAddress, ChannelDiscovery::ChannelMap& channelMap);
};

}}

#endif // NAME_SERVER_IMPL_H
234 changes: 234 additions & 0 deletions pvtoolsSrc/pvans.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/

/*
* PVA Name Server utility. It polls a set of PVA
* servers for a list of channels, and resolves channel queries.
* PVA servers can be discovered, they can be passed through the
* command line, or they can be specified via an input file which
* gets re-read at runtime. The utility also supports static channel
* map entries read from an input file.
*/

#include <stdio.h>

#include <iostream>
#include <string>
#include <istream>
#include <fstream>
#include <sstream>
#include <vector>

#include <epicsStdlib.h>
#include <epicsGetopt.h>
#include <epicsExit.h>

#include <pv/logger.h>
#include <pv/configuration.h>
#include <pv/stringUtility.h>

#include "nameServerImpl.h"

using namespace std;

using namespace epics::pvData;
using namespace epics::pvAccess;
using epics::pvAccess::ChannelDiscovery::ChannelEntry;
using epics::pvAccess::ChannelDiscovery::ChannelMap;

namespace {

#define DEFAULT_PVA_TIMEOUT 3.0
#define DEFAULT_POLL_PERIOD 900.0
#define DEFAULT_CHANNEL_EXPIRATION_TIME 2*DEFAULT_POLL_PERIOD

void usage (void) {
fprintf (stderr,
"\nPVA Name Server\n"
"\nUsage: pvans [options]...\n\n"
"\noptions:\n"
" -h|-H\t\t\t:\tHelp: Print this message\n"
" -V\t\t\t:\tPrint version and exit\n"
" -f <input file>\t:\tStatic server list file with '<HOST:PORT>' entries\n"
" -F <input file>\t:\tStatic channel map file with '<CHANNEL> <HOST:PORT>' entries\n"
" -s <addr>,<addr>,...\t:\tComma-separated list of '<HOST:PORT>' static server entries\n"
" -a\t\t\t:\tAuto mode, discover severs available on the network\n"
" -p <poll period>\t:\tServer poll period in seconds (default: %.2f [s])\n"
" -w <wait period>\t:\tServer wait time in seconds (default: %.2f [s])\n"
" -e <expiration time>\t:\tChannel entry expiration time in seconds (default: %.2f [s])\n"
" -d\t\t\t:\tEnable debug output\n"
"\nDifferent inputs for PVA server address will be combined."
"\nServer list file should contain '<HOST:PORT>' entries separated by spaces or commas, or on different lines."
"\nChannel map file should contain '<CHANNEL> <HOST:PORT>' entries separated by spaces or commas, or on different lines."
"\nChannel expiration time <= 0 indicates that channel entries"
"\nnever expire.\n\n"
, DEFAULT_POLL_PERIOD, DEFAULT_PVA_TIMEOUT, DEFAULT_CHANNEL_EXPIRATION_TIME);
}

// Expected server address format: <HOST:PORT>
// There can be multiple addresses per line, separated by spaces or commas.
std::string readServerAddressesFromFile(const std::string& inputFile, const std::string& existingAddresses = "")
{
std::string serverAddresses = existingAddresses;
if (inputFile.empty()) {
return serverAddresses;
}
std::ifstream ifs(inputFile.c_str());
std::string line;
while (std::getline(ifs, line)) {
line = StringUtility::replace(line, ',', " ");
serverAddresses = serverAddresses + " " + line;
}
return serverAddresses;
}

// Expected channel entry format: <CHANNEL_NAME> <HOST:PORT>
// There can be multiple entries per line, separated by spaces or commas.
void readChannelAddressesFromFile(const std::string& inputFile, ChannelMap& channelMap)
{
if (inputFile.empty()) {
return;
}
bool ignoreEmptyTokens = true;
epicsTimeStamp now;
epicsTimeGetCurrent(&now);
std::ifstream ifs(inputFile.c_str());
std::string line;
while (std::getline(ifs, line)) {
line = StringUtility::replace(line, ',', " ");
std::vector<std::string> tokens = StringUtility::split(line, ' ', ignoreEmptyTokens);
int nTokens = int(tokens.size());
for (int i = 0; i < nTokens-1; i+=2) {
std::string channelName = tokens[i];
std::string serverAddress = tokens[i+1];
ChannelEntry channelEntry(channelName, serverAddress, now);
channelMap[channelName] = channelEntry;
LOG(logLevelDebug, "Adding %s/%s channel entry", channelName.c_str(), serverAddress.c_str());
}
}
}

} // namespace

int main(int argc, char *argv[])
{
int opt; /* getopt() current option */
bool debug = false;
bool autoDiscovery = false;
double timeout = DEFAULT_PVA_TIMEOUT;
double pollPeriod = DEFAULT_POLL_PERIOD;
double channelExpirationTime = DEFAULT_CHANNEL_EXPIRATION_TIME;
std::string serverAddresses;
std::string serverListFile;
std::string channelMapFile;

while ((opt = getopt(argc, argv, ":hHVw:e:p:das:f:F:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
case 'H': { /* Print usage */
usage();
return 0;
}
case 'V': { /* Print version */
fprintf(stdout, "pvAccess %u.%u.%u%s\n",
EPICS_PVA_MAJOR_VERSION,
EPICS_PVA_MINOR_VERSION,
EPICS_PVA_MAINTENANCE_VERSION,
(EPICS_PVA_DEVELOPMENT_FLAG)?"-SNAPSHOT":"");
fprintf(stdout, "pvData %u.%u.%u%s\n",
EPICS_PVD_MAJOR_VERSION,
EPICS_PVD_MINOR_VERSION,
EPICS_PVD_MAINTENANCE_VERSION,
(EPICS_PVD_DEVELOPMENT_FLAG)?"-SNAPSHOT":"");
fprintf(stdout, "Base %s\n", EPICS_VERSION_FULL);
return 0;
}
case 'p': { /* Set poll period */
if((epicsScanDouble(optarg, &pollPeriod)) != 1 || pollPeriod <= 0.0) {
fprintf(stderr,
"'%s' is not a valid poll period value "
"- ignored. ('pvans -h' for help.)\n", optarg);
timeout = DEFAULT_PVA_TIMEOUT;
}
break;
}
case 'w': { /* Set PVA timeout value */
if((epicsScanDouble(optarg, &timeout)) != 1 || timeout <= 0.0) {
fprintf(stderr,
"'%s' is not a valid timeout value "
"- ignored. ('pvans -h' for help.)\n", optarg);
timeout = DEFAULT_PVA_TIMEOUT;
}
break;
}
case 'e': { /* Set channel expiration time */
if((epicsScanDouble(optarg, &channelExpirationTime)) != 1) {
fprintf(stderr,
"'%s' is not a valid expiration time value "
"- ignored. ('pvans -h' for help.)\n", optarg);
channelExpirationTime = DEFAULT_CHANNEL_EXPIRATION_TIME;
}
break;
}
case 's': { /* Server list */
serverAddresses = optarg;
break;
}
case 'f': { /* Server list file */
serverListFile = optarg;
break;
}
case 'F': { /* Channel map file */
channelMapFile = optarg;
break;
}
case 'd': { /* Debug log level */
debug = true;
break;
}
case 'a': { /* Auto discovery */
autoDiscovery = true;
break;
}
case '?': {
fprintf(stderr,
"Unrecognized option: '-%c'. ('pvans -h' for help.)\n",
optopt);
return 1;
}
case ':': {
fprintf(stderr,
"Option '-%c' requires an argument. ('pvans -h' for help.)\n",
optopt);
return 1;
}
default: {
usage();
return 1;
}
}
}

SET_LOG_LEVEL(debug ? logLevelDebug : logLevelError);

NameServerImpl::shared_pointer srv(new NameServerImpl(ConfigurationBuilder()
.push_env()
.push_map()
.build()));
srv->setPollPeriod(pollPeriod);
srv->setPvaTimeout(timeout);
srv->setAutoDiscovery(autoDiscovery);
srv->setChannelEntryExpirationTime(channelExpirationTime);
while (true) {
// Reread input file before polling.
std::string staticServerAddresses = readServerAddressesFromFile(serverListFile, serverAddresses);
srv->setStaticServerAddresses(staticServerAddresses);
ChannelMap staticChannelMap;
readChannelAddressesFromFile(channelMapFile, staticChannelMap);
srv->setStaticChannelEntries(staticChannelMap);
srv->run(pollPeriod);
}
return 0;
}
Loading
Loading