Skip to content

Commit

Permalink
Merge 7832fc4 into f1268ad
Browse files Browse the repository at this point in the history
  • Loading branch information
sveseli committed Mar 31, 2024
2 parents f1268ad + 7832fc4 commit 91e0d54
Show file tree
Hide file tree
Showing 49 changed files with 3,110 additions and 569 deletions.
7 changes: 7 additions & 0 deletions pvtoolsSrc/Makefile
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
92 changes: 92 additions & 0 deletions pvtoolsSrc/nameServerImpl.cpp
@@ -0,0 +1,92 @@
/**
* 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/logger.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 + inetAddrToString(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 = inetAddrToString(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
@@ -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
@@ -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;
}

0 comments on commit 91e0d54

Please sign in to comment.