Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
50 changed files
with
3,128 additions
and
573 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()); | ||
} | ||
} | ||
|
||
}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.