Rename pvAccessApp to src, adjust Makefiles
This commit is contained in:
28
src/Makefile
Normal file
28
src/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
PVACCESS_SRC = $(TOP)/src
|
||||
|
||||
include $(PVACCESS_SRC)/pva/Makefile
|
||||
include $(PVACCESS_SRC)/utils/Makefile
|
||||
include $(PVACCESS_SRC)/client/Makefile
|
||||
include $(PVACCESS_SRC)/factory/Makefile
|
||||
include $(PVACCESS_SRC)/remote/Makefile
|
||||
include $(PVACCESS_SRC)/remoteClient/Makefile
|
||||
include $(PVACCESS_SRC)/server/Makefile
|
||||
include $(PVACCESS_SRC)/rpcService/Makefile
|
||||
include $(PVACCESS_SRC)/rpcClient/Makefile
|
||||
include $(PVACCESS_SRC)/ca/Makefile
|
||||
include $(PVACCESS_SRC)/mb/Makefile
|
||||
include $(PVACCESS_SRC)/v3ioc/Makefile
|
||||
|
||||
LIBRARY = pvAccess
|
||||
|
||||
pvAccess_LIBS += pvData
|
||||
pvAccess_LIBS += ca
|
||||
pvAccess_LIBS += Com
|
||||
|
||||
# needed for Windows
|
||||
pvAccess_SYS_LIBS_WIN32 += ws2_32
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
9
src/ca/Makefile
Normal file
9
src/ca/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/ca
|
||||
|
||||
INC += caProvider.h
|
||||
INC += caChannel.h
|
||||
|
||||
LIBSRCS += caProvider.cpp
|
||||
LIBSRCS += caChannel.cpp
|
||||
1362
src/ca/caChannel.cpp
Normal file
1362
src/ca/caChannel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
275
src/ca/caChannel.h
Normal file
275
src/ca/caChannel.h
Normal file
@@ -0,0 +1,275 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CACHANNEL_H
|
||||
#define CACHANNEL_H
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
/* for CA */
|
||||
#include <cadef.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
class CAChannel :
|
||||
public Channel,
|
||||
public std::tr1::enable_shared_from_this<CAChannel>
|
||||
{
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(CAChannel);
|
||||
|
||||
static shared_pointer create(ChannelProvider::shared_pointer const & channelProvider,
|
||||
epics::pvData::String const & channelName,
|
||||
short priority,
|
||||
ChannelRequester::shared_pointer const & channelRequester);
|
||||
|
||||
virtual ~CAChannel();
|
||||
|
||||
void connected();
|
||||
void disconnected();
|
||||
|
||||
chid getChannelID();
|
||||
chtype getNativeType();
|
||||
unsigned getElementCount();
|
||||
|
||||
/* --------------- epics::pvAccess::Channel --------------- */
|
||||
|
||||
virtual std::tr1::shared_ptr<ChannelProvider> getProvider();
|
||||
virtual epics::pvData::String getRemoteAddress();
|
||||
virtual ConnectionState getConnectionState();
|
||||
virtual epics::pvData::String getChannelName();
|
||||
virtual std::tr1::shared_ptr<ChannelRequester> getChannelRequester();
|
||||
virtual bool isConnected();
|
||||
|
||||
virtual void getField(GetFieldRequester::shared_pointer const & requester,epics::pvData::String const & subField);
|
||||
|
||||
virtual AccessRights getAccessRights(epics::pvData::PVField::shared_pointer const & pvField);
|
||||
|
||||
virtual ChannelProcess::shared_pointer createChannelProcess(
|
||||
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual ChannelGet::shared_pointer createChannelGet(
|
||||
ChannelGetRequester::shared_pointer const & channelGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual ChannelPut::shared_pointer createChannelPut(
|
||||
ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual ChannelPutGet::shared_pointer createChannelPutGet(
|
||||
ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual ChannelRPC::shared_pointer createChannelRPC(
|
||||
ChannelRPCRequester::shared_pointer const & channelRPCRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual epics::pvData::Monitor::shared_pointer createMonitor(
|
||||
epics::pvData::MonitorRequester::shared_pointer const & monitorRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual ChannelArray::shared_pointer createChannelArray(
|
||||
ChannelArrayRequester::shared_pointer const & channelArrayRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual void printInfo();
|
||||
|
||||
virtual void printInfo(epics::pvData::StringBuilder out);
|
||||
|
||||
/* --------------- epics::pvData::Requester --------------- */
|
||||
|
||||
virtual epics::pvData::String getRequesterName();
|
||||
|
||||
virtual void message(epics::pvData::String const & message, epics::pvData::MessageType messageType);
|
||||
|
||||
/* --------------- epics::pvData::Destroyable --------------- */
|
||||
|
||||
virtual void destroy();
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
void registerRequest(ChannelRequest::shared_pointer const & request);
|
||||
void unregisterRequest(ChannelRequest::shared_pointer const & request);
|
||||
|
||||
private:
|
||||
|
||||
CAChannel(epics::pvData::String const & channelName,
|
||||
ChannelProvider::shared_pointer const & channelProvider,
|
||||
ChannelRequester::shared_pointer const & channelRequester);
|
||||
void activate(short priority);
|
||||
|
||||
epics::pvData::String channelName;
|
||||
|
||||
ChannelProvider::shared_pointer channelProvider;
|
||||
ChannelRequester::shared_pointer channelRequester;
|
||||
|
||||
chid channelID;
|
||||
chtype channelType;
|
||||
unsigned elementCount;
|
||||
epics::pvData::Structure::const_shared_pointer structure;
|
||||
|
||||
epics::pvData::Mutex requestsMutex;
|
||||
// TODO std::unordered_map
|
||||
// void* is not the nicest thing, but there is no fast weak_ptr==
|
||||
typedef std::map<void*, ChannelRequest::weak_pointer> RequestsList;
|
||||
RequestsList requests;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CAChannelGet :
|
||||
public ChannelGet,
|
||||
public std::tr1::enable_shared_from_this<CAChannelGet>
|
||||
{
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(CAChannelGet);
|
||||
|
||||
static ChannelGet::shared_pointer create(CAChannel::shared_pointer const & channel,
|
||||
ChannelGetRequester::shared_pointer const & channelGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual ~CAChannelGet();
|
||||
|
||||
void getDone(struct event_handler_args &args);
|
||||
|
||||
/* --------------- epics::pvAccess::ChannelGet --------------- */
|
||||
|
||||
virtual void get(bool lastRequest);
|
||||
|
||||
/* --------------- epics::pvData::Destroyable --------------- */
|
||||
|
||||
virtual void destroy();
|
||||
|
||||
/* --------------- epics::pvData::Lockable --------------- */
|
||||
|
||||
virtual void lock();
|
||||
virtual void unlock();
|
||||
|
||||
private:
|
||||
|
||||
CAChannelGet(CAChannel::shared_pointer const & _channel,
|
||||
ChannelGetRequester::shared_pointer const & _channelGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
void activate();
|
||||
|
||||
CAChannel::shared_pointer channel;
|
||||
ChannelGetRequester::shared_pointer channelGetRequester;
|
||||
chtype getType;
|
||||
|
||||
epics::pvData::PVStructure::shared_pointer pvStructure;
|
||||
epics::pvData::BitSet::shared_pointer bitSet;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CAChannelPut :
|
||||
public ChannelPut,
|
||||
public std::tr1::enable_shared_from_this<CAChannelPut>
|
||||
{
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(CAChannelPut);
|
||||
|
||||
static ChannelPut::shared_pointer create(CAChannel::shared_pointer const & channel,
|
||||
ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual ~CAChannelPut();
|
||||
|
||||
void putDone(struct event_handler_args &args);
|
||||
void getDone(struct event_handler_args &args);
|
||||
|
||||
/* --------------- epics::pvAccess::ChannelPut --------------- */
|
||||
|
||||
virtual void put(bool lastRequest);
|
||||
virtual void get();
|
||||
|
||||
/* --------------- epics::pvData::Destroyable --------------- */
|
||||
|
||||
virtual void destroy();
|
||||
|
||||
/* --------------- epics::pvData::Lockable --------------- */
|
||||
|
||||
virtual void lock();
|
||||
virtual void unlock();
|
||||
|
||||
private:
|
||||
|
||||
CAChannelPut(CAChannel::shared_pointer const & _channel,
|
||||
ChannelPutRequester::shared_pointer const & _channelPutRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
void activate();
|
||||
|
||||
CAChannel::shared_pointer channel;
|
||||
ChannelPutRequester::shared_pointer channelPutRequester;
|
||||
chtype getType;
|
||||
|
||||
epics::pvData::PVStructure::shared_pointer pvStructure;
|
||||
epics::pvData::BitSet::shared_pointer bitSet;
|
||||
};
|
||||
|
||||
|
||||
class CAChannelMonitor :
|
||||
public epics::pvData::Monitor,
|
||||
public std::tr1::enable_shared_from_this<CAChannelMonitor>
|
||||
{
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(CAChannelMonitor);
|
||||
|
||||
static epics::pvData::Monitor::shared_pointer create(CAChannel::shared_pointer const & channel,
|
||||
epics::pvData::MonitorRequester::shared_pointer const & monitorRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual ~CAChannelMonitor();
|
||||
|
||||
void subscriptionEvent(struct event_handler_args &args);
|
||||
|
||||
/* --------------- epics::pvData::Monitor --------------- */
|
||||
|
||||
virtual epics::pvData::Status start();
|
||||
virtual epics::pvData::Status stop();
|
||||
virtual epics::pvData::MonitorElementPtr poll();
|
||||
virtual void release(epics::pvData::MonitorElementPtr const & monitorElement);
|
||||
|
||||
/* --------------- epics::pvData::Destroyable --------------- */
|
||||
|
||||
virtual void destroy();
|
||||
|
||||
private:
|
||||
|
||||
CAChannelMonitor(CAChannel::shared_pointer const & _channel,
|
||||
epics::pvData::MonitorRequester::shared_pointer const & _monitorRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
void activate();
|
||||
|
||||
CAChannel::shared_pointer channel;
|
||||
epics::pvData::MonitorRequester::shared_pointer monitorRequester;
|
||||
chtype getType;
|
||||
|
||||
epics::pvData::PVStructure::shared_pointer pvStructure;
|
||||
epics::pvData::BitSet::shared_pointer changedBitSet;
|
||||
epics::pvData::BitSet::shared_pointer overrunBitSet;
|
||||
evid eventID;
|
||||
|
||||
epics::pvData::Mutex mutex;
|
||||
int count;
|
||||
|
||||
epics::pvData::MonitorElement::shared_pointer element;
|
||||
epics::pvData::MonitorElement::shared_pointer nullElement;
|
||||
|
||||
// TODO remove
|
||||
Monitor::shared_pointer thisPointer;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif /* CACHANNEL_H */
|
||||
216
src/ca/caProvider.cpp
Normal file
216
src/ca/caProvider.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* 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 <algorithm>
|
||||
|
||||
/* for CA */
|
||||
#include <cadef.h>
|
||||
#include <epicsSignal.h>
|
||||
|
||||
#include <pv/logger.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include <pv/caProvider.h>
|
||||
#include <pv/caChannel.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
using namespace epics::pvAccess::ca;
|
||||
|
||||
#define EXCEPTION_GUARD(code) try { code; } \
|
||||
catch (std::exception &e) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d: %s", __FILE__, __LINE__, e.what()); } \
|
||||
catch (...) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d.", __FILE__, __LINE__); }
|
||||
|
||||
String CAChannelProvider::PROVIDER_NAME = "ca";
|
||||
|
||||
CAChannelProvider::CAChannelProvider()
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
CAChannelProvider::~CAChannelProvider()
|
||||
{
|
||||
}
|
||||
|
||||
epics::pvData::String CAChannelProvider::getProviderName()
|
||||
{
|
||||
return PROVIDER_NAME;
|
||||
}
|
||||
|
||||
ChannelFind::shared_pointer CAChannelProvider::channelFind(
|
||||
epics::pvData::String const & channelName,
|
||||
ChannelFindRequester::shared_pointer const & channelFindRequester)
|
||||
{
|
||||
if (channelName.empty())
|
||||
throw std::invalid_argument("empty channel name");
|
||||
|
||||
if (!channelFindRequester)
|
||||
throw std::invalid_argument("null requester");
|
||||
|
||||
Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented");
|
||||
ChannelFind::shared_pointer nullChannelFind;
|
||||
EXCEPTION_GUARD(channelFindRequester->channelFindResult(errorStatus, nullChannelFind, false));
|
||||
return nullChannelFind;
|
||||
}
|
||||
|
||||
Channel::shared_pointer CAChannelProvider::createChannel(
|
||||
epics::pvData::String const & channelName,
|
||||
ChannelRequester::shared_pointer const & channelRequester,
|
||||
short priority)
|
||||
{
|
||||
static String emptyString;
|
||||
return createChannel(channelName, channelRequester, priority, emptyString);
|
||||
}
|
||||
|
||||
Channel::shared_pointer CAChannelProvider::createChannel(
|
||||
epics::pvData::String const & channelName,
|
||||
ChannelRequester::shared_pointer const & channelRequester,
|
||||
short priority,
|
||||
epics::pvData::String const & address)
|
||||
{
|
||||
if (!address.empty())
|
||||
throw std::invalid_argument("CA does not support 'address' parameter");
|
||||
|
||||
return CAChannel::create(shared_from_this(), channelName, priority, channelRequester);
|
||||
}
|
||||
|
||||
void CAChannelProvider::configure(epics::pvData::PVStructure::shared_pointer /*configuration*/)
|
||||
{
|
||||
}
|
||||
|
||||
void CAChannelProvider::flush()
|
||||
{
|
||||
}
|
||||
|
||||
void CAChannelProvider::poll()
|
||||
{
|
||||
}
|
||||
|
||||
void CAChannelProvider::destroy()
|
||||
{
|
||||
Lock lock(channelsMutex);
|
||||
{
|
||||
while (!channels.empty())
|
||||
{
|
||||
Channel::shared_pointer channel = channels.rbegin()->second.lock();
|
||||
if (channel)
|
||||
channel->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/* Destroy CA Context */
|
||||
ca_context_destroy();
|
||||
}
|
||||
|
||||
void CAChannelProvider::registerChannel(Channel::shared_pointer const & channel)
|
||||
{
|
||||
Lock lock(channelsMutex);
|
||||
channels[channel.get()] = Channel::weak_pointer(channel);
|
||||
}
|
||||
|
||||
void CAChannelProvider::unregisterChannel(Channel::shared_pointer const & channel)
|
||||
{
|
||||
Lock lock(channelsMutex);
|
||||
channels.erase(channel.get());
|
||||
}
|
||||
|
||||
void CAChannelProvider::initialize()
|
||||
{
|
||||
/* Create Channel Access */
|
||||
int result = ca_context_create(ca_enable_preemptive_callback);
|
||||
if (result != ECA_NORMAL) {
|
||||
throw std::runtime_error(std::string("CA error %s occurred while trying "
|
||||
"to start channel access:") + ca_message(result));
|
||||
}
|
||||
|
||||
// TODO create a ca_poll thread, if ca_disable_preemptive_callback
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO global static variable (de/initialization order not guaranteed)
|
||||
static Mutex mutex;
|
||||
static CAChannelProvider::shared_pointer sharedProvider;
|
||||
|
||||
class CAChannelProviderFactoryImpl : public ChannelProviderFactory
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(CAChannelProviderFactoryImpl);
|
||||
|
||||
virtual epics::pvData::String getFactoryName()
|
||||
{
|
||||
return CAChannelProvider::PROVIDER_NAME;
|
||||
}
|
||||
|
||||
virtual ChannelProvider::shared_pointer sharedInstance()
|
||||
{
|
||||
Lock guard(mutex);
|
||||
if (!sharedProvider.get())
|
||||
{
|
||||
try {
|
||||
sharedProvider.reset(new CAChannelProvider());
|
||||
} catch (std::exception &e) {
|
||||
LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what());
|
||||
} catch (...) {
|
||||
LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
return sharedProvider;
|
||||
}
|
||||
|
||||
virtual ChannelProvider::shared_pointer newInstance()
|
||||
{
|
||||
try {
|
||||
return ChannelProvider::shared_pointer(new CAChannelProvider());
|
||||
} catch (std::exception &e) {
|
||||
LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what());
|
||||
return ChannelProvider::shared_pointer();
|
||||
} catch (...) {
|
||||
LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__);
|
||||
return ChannelProvider::shared_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
void destroySharedInstance()
|
||||
{
|
||||
sharedProvider->destroy();
|
||||
sharedProvider.reset();
|
||||
}
|
||||
};
|
||||
|
||||
static CAChannelProviderFactoryImpl::shared_pointer factory;
|
||||
|
||||
void CAClientFactory::start()
|
||||
{
|
||||
epicsSignalInstallSigAlarmIgnore();
|
||||
epicsSignalInstallSigPipeIgnore();
|
||||
|
||||
Lock guard(mutex);
|
||||
if (!factory.get())
|
||||
factory.reset(new CAChannelProviderFactoryImpl());
|
||||
|
||||
registerChannelProviderFactory(factory);
|
||||
}
|
||||
|
||||
void CAClientFactory::stop()
|
||||
{
|
||||
Lock guard(mutex);
|
||||
|
||||
if (factory.get())
|
||||
{
|
||||
unregisterChannelProviderFactory(factory);
|
||||
factory->destroySharedInstance();
|
||||
}
|
||||
}
|
||||
82
src/ca/caProvider.h
Normal file
82
src/ca/caProvider.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CAPROVIDER_H
|
||||
#define CAPROVIDER_H
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
#include <map>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace ca {
|
||||
|
||||
class epicsShareClass CAChannelProvider :
|
||||
public ChannelProvider,
|
||||
public std::tr1::enable_shared_from_this<CAChannelProvider>
|
||||
{
|
||||
public:
|
||||
|
||||
static epics::pvData::String PROVIDER_NAME;
|
||||
|
||||
CAChannelProvider();
|
||||
virtual ~CAChannelProvider();
|
||||
|
||||
/* --------------- epics::pvAccess::ChannelProvider --------------- */
|
||||
|
||||
virtual epics::pvData::String getProviderName();
|
||||
|
||||
virtual ChannelFind::shared_pointer channelFind(
|
||||
epics::pvData::String const & channelName,
|
||||
ChannelFindRequester::shared_pointer const & channelFindRequester);
|
||||
|
||||
|
||||
virtual Channel::shared_pointer createChannel(
|
||||
epics::pvData::String const & channelName,
|
||||
ChannelRequester::shared_pointer const & channelRequester,
|
||||
short priority);
|
||||
|
||||
virtual Channel::shared_pointer createChannel(
|
||||
epics::pvData::String const & channelName,
|
||||
ChannelRequester::shared_pointer const & channelRequester,
|
||||
short priority,
|
||||
epics::pvData::String const & address);
|
||||
|
||||
virtual void configure(epics::pvData::PVStructure::shared_pointer configuration);
|
||||
virtual void flush();
|
||||
virtual void poll();
|
||||
|
||||
virtual void destroy();
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
void registerChannel(Channel::shared_pointer const & channel);
|
||||
void unregisterChannel(Channel::shared_pointer const & channel);
|
||||
|
||||
private:
|
||||
|
||||
void initialize();
|
||||
|
||||
epics::pvData::Mutex channelsMutex;
|
||||
// TODO std::unordered_map
|
||||
// void* is not the nicest thing, but there is no fast weak_ptr==
|
||||
typedef std::map<void*, Channel::weak_pointer> ChannelList;
|
||||
ChannelList channels;
|
||||
};
|
||||
|
||||
|
||||
class epicsShareClass CAClientFactory
|
||||
{
|
||||
public:
|
||||
static void start();
|
||||
static void stop();
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif /* CAPROVIDER_H */
|
||||
7
src/client/Makefile
Normal file
7
src/client/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/client
|
||||
|
||||
INC += pvAccess.h
|
||||
|
||||
LIBSRCS += pvAccess.cpp
|
||||
15
src/client/pvAccess.cpp
Normal file
15
src/client/pvAccess.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
const char* Channel::ConnectionStateNames[] = { "NEVER_CONNECTED", "CONNECTED", "DISCONNECTED", "DESTROYED" };
|
||||
|
||||
}}
|
||||
838
src/client/pvAccess.h
Normal file
838
src/client/pvAccess.h
Normal file
@@ -0,0 +1,838 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PVACCESS_H
|
||||
#define PVACCESS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define pvAccessEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/status.h>
|
||||
#include <pv/destroyable.h>
|
||||
#include <pv/monitor.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#ifdef pvAccessEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef pvAccessEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvaVersion.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
// TODO add write-only?
|
||||
// change names
|
||||
enum AccessRights {
|
||||
/**
|
||||
* Neither read or write access is allowed.
|
||||
*/
|
||||
none,
|
||||
/**
|
||||
* Read access is allowed but write access is not allowed.
|
||||
*/
|
||||
read,
|
||||
/**
|
||||
* Both read and write access are allowed.
|
||||
*/
|
||||
readWrite
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class epicsShareClass Lockable
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(Lockable);
|
||||
|
||||
virtual ~Lockable() {};
|
||||
|
||||
virtual void lock() = 0;
|
||||
virtual void unlock() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Scope lock.
|
||||
*/
|
||||
class epicsShareClass ScopedLock : private epics::pvData::NoDefaultMethods {
|
||||
public:
|
||||
|
||||
explicit ScopedLock(Lockable::shared_pointer const & li)
|
||||
: lockable(li), locked(true) {
|
||||
lockable->lock();
|
||||
}
|
||||
|
||||
~ScopedLock() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
void lock() {
|
||||
if(!locked) {
|
||||
lockable->lock();
|
||||
locked = true;
|
||||
}
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
if(locked) {
|
||||
lockable->unlock();
|
||||
locked=false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ownsLock() const {
|
||||
return locked;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Lockable::shared_pointer const lockable;
|
||||
bool locked;
|
||||
};
|
||||
|
||||
|
||||
class Channel;
|
||||
class ChannelProvider;
|
||||
|
||||
/**
|
||||
* Base interface for all channel requests.
|
||||
*/
|
||||
class epicsShareClass ChannelRequest : public epics::pvData::Destroyable, public Lockable, private epics::pvData::NoDefaultMethods {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelRequest);
|
||||
};
|
||||
|
||||
/**
|
||||
* Request to put and get Array Data.
|
||||
* The data is either taken from or put in the PVArray returned by ChannelArrayRequester.channelArrayConnect.
|
||||
*/
|
||||
class epicsShareClass ChannelArray : public ChannelRequest{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelArray);
|
||||
|
||||
/**
|
||||
* put to the remote array.
|
||||
* @param lastRequest Is this the last request.
|
||||
* @param offset The offset in the remote array, i.e. the PVArray returned by ChannelArrayRequester.channelArrayConnect.
|
||||
* @param count The number of elements to put.
|
||||
*/
|
||||
virtual void putArray(bool lastRequest, int offset, int count) = 0;
|
||||
|
||||
/**
|
||||
* get from the remote array.
|
||||
* @param lastRequest Is this the last request.
|
||||
* @param offset The offset in the remote array, i.e. the PVArray returned by ChannelArrayRequester.channelArrayConnect.
|
||||
* @param count The number of elements to get.
|
||||
*/
|
||||
virtual void getArray(bool lastRequest, int offset, int count) = 0;
|
||||
|
||||
/**
|
||||
* Set the length and/or the capacity.
|
||||
* @param lastRequest Is this the last request.
|
||||
* @param length The new length. -1 means do not change.
|
||||
* @param capacity The new capacity. -1 means do not change.
|
||||
*/
|
||||
virtual void setLength(bool lastRequest, int length, int capacity) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* The epics::pvData::Requester for a ChannelArray.
|
||||
*/
|
||||
class epicsShareClass ChannelArrayRequester : virtual public epics::pvData::Requester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelArrayRequester);
|
||||
|
||||
/**
|
||||
* The client and server have both completed the createChannelArray request.
|
||||
* @param status Completion status.
|
||||
* @param channelArray The channelArray interface or null if the request failed.
|
||||
* @param pvArray The PVArray that holds the data.
|
||||
*/
|
||||
virtual void channelArrayConnect(
|
||||
const epics::pvData::Status& status,
|
||||
ChannelArray::shared_pointer const & channelArray,
|
||||
epics::pvData::PVArray::shared_pointer const & pvArray) = 0;
|
||||
|
||||
/**
|
||||
* The request is done. This is always called with no locks held.
|
||||
* @param status Completion status.
|
||||
*/
|
||||
virtual void putArrayDone(const epics::pvData::Status& status) = 0;
|
||||
|
||||
/**
|
||||
* The request is done. This is always called with no locks held.
|
||||
* @param status Completion status.
|
||||
*/
|
||||
virtual void getArrayDone(const epics::pvData::Status& status) = 0;
|
||||
|
||||
/**
|
||||
* The request is done. This is always called with no locks held.
|
||||
* @param status Completion status.
|
||||
*/
|
||||
virtual void setLengthDone(const epics::pvData::Status& status) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class epicsShareClass ChannelFind : public epics::pvData::Destroyable, private epics::pvData::NoDefaultMethods {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelFind);
|
||||
|
||||
virtual std::tr1::shared_ptr<ChannelProvider> getChannelProvider() = 0;
|
||||
virtual void cancelChannelFind() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class epicsShareClass ChannelFindRequester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelFindRequester);
|
||||
|
||||
virtual ~ChannelFindRequester() {};
|
||||
/**
|
||||
* @param status Completion status.
|
||||
*/
|
||||
virtual void channelFindResult(const epics::pvData::Status& status,ChannelFind::shared_pointer const & channelFind,bool wasFound) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Request to get data from a channel.
|
||||
*/
|
||||
class epicsShareClass ChannelGet : public ChannelRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelGet);
|
||||
|
||||
/**
|
||||
* Get data from the channel.
|
||||
* This fails if the request can not be satisfied.
|
||||
* If it fails ChannelGetRequester.getDone is called before get returns.
|
||||
* @param lastRequest Is this the last request?
|
||||
*/
|
||||
virtual void get(bool lastRequest) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* epics::pvData::Requester for channelGet.
|
||||
*/
|
||||
class epicsShareClass ChannelGetRequester : virtual public epics::pvData::Requester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelGetRequester);
|
||||
|
||||
/**
|
||||
* The client and server have both completed the createChannelGet request.
|
||||
* @param status Completion status.
|
||||
* @param channelGet The channelGet interface or null if the request failed.
|
||||
* @param pvStructure The PVStructure that holds the data.
|
||||
* @param bitSet The bitSet for that shows what data has changed.
|
||||
*/
|
||||
virtual void channelGetConnect(const epics::pvData::Status& status,ChannelGet::shared_pointer const & channelGet,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvStructure,epics::pvData::BitSet::shared_pointer const & bitSet) = 0;
|
||||
|
||||
/**
|
||||
* The request is done. This is always called with no locks held.
|
||||
* @param status Completion status.
|
||||
*/
|
||||
virtual void getDone(const epics::pvData::Status& status) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* ChannelProcess - request that a channel be processed..
|
||||
*/
|
||||
class epicsShareClass ChannelProcess : public ChannelRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelProcess);
|
||||
|
||||
/**
|
||||
* Issue a process request.
|
||||
* This fails if the request can not be satisfied.
|
||||
* If it fails the channelProcessRequester.processDone is called before process returns.
|
||||
* @param lastRequest Is this the last request?
|
||||
*/
|
||||
virtual void process(bool lastRequest) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* epics::pvData::Requester for channelProcess.
|
||||
*/
|
||||
class epicsShareClass ChannelProcessRequester : virtual public epics::pvData::Requester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelProcessRequester);
|
||||
|
||||
/**
|
||||
* The client and server have both completed the createChannelProcess request.
|
||||
* @param status Completion status.
|
||||
* @param channelProcess The channelProcess interface or null if the client could not become
|
||||
* the record processor.
|
||||
*/
|
||||
virtual void channelProcessConnect(const epics::pvData::Status& status,ChannelProcess::shared_pointer const & channelProcess) = 0;
|
||||
|
||||
/**
|
||||
* The process request is done. This is always called with no locks held.
|
||||
* @param status Completion status.
|
||||
*/
|
||||
virtual void processDone(const epics::pvData::Status& status) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Interface for a channel access put request.
|
||||
*/
|
||||
class epicsShareClass ChannelPut : public ChannelRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelPut);
|
||||
|
||||
/**
|
||||
* Put data to a channel.
|
||||
* This fails if the request can not be satisfied.
|
||||
* If it fails ChannelPutRequester.putDone is called before put returns.
|
||||
* @param lastRequest Is this the last request?
|
||||
*/
|
||||
virtual void put(bool lastRequest) = 0;
|
||||
|
||||
/**
|
||||
* Get the current data.
|
||||
*/
|
||||
virtual void get() = 0;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* epics::pvData::Requester for ChannelPut.
|
||||
*/
|
||||
class epicsShareClass ChannelPutRequester : virtual public epics::pvData::Requester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelPutRequester);
|
||||
|
||||
/**
|
||||
* The client and server have both processed the createChannelPut request.
|
||||
* @param status Completion status.
|
||||
* @param channelPut The channelPut interface or null if the request failed.
|
||||
* @param pvStructure The PVStructure that holds the data.
|
||||
* @param bitSet The bitSet for that shows what data has changed.
|
||||
*/
|
||||
virtual void channelPutConnect(const epics::pvData::Status& status,ChannelPut::shared_pointer const & channelPut,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvStructure,epics::pvData::BitSet::shared_pointer const & bitSet) = 0;
|
||||
|
||||
/**
|
||||
* The request is done. This is always called with no locks held.
|
||||
* @param status Completion status.
|
||||
*/
|
||||
virtual void putDone(const epics::pvData::Status& status) = 0;
|
||||
|
||||
/**
|
||||
* The get request is done. This is always called with no locks held.
|
||||
* @param status Completion status.
|
||||
*/
|
||||
virtual void getDone(const epics::pvData::Status& status) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Channel access put/get request.
|
||||
* The put is performed first, followed optionally by a process request, and then by a get request.
|
||||
*/
|
||||
class epicsShareClass ChannelPutGet : public ChannelRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelPutGet);
|
||||
|
||||
/**
|
||||
* Issue a put/get request. If process was requested when the ChannelPutGet was created this is a put, process, get.
|
||||
* This fails if the request can not be satisfied.
|
||||
* If it fails ChannelPutGetRequester.putDone is called before putGet returns.
|
||||
* @param lastRequest Is this the last request?
|
||||
*/
|
||||
virtual void putGet(bool lastRequest) = 0;
|
||||
|
||||
/**
|
||||
* Get the put PVStructure. The record will not be processed.
|
||||
*/
|
||||
virtual void getPut() = 0;
|
||||
|
||||
/**
|
||||
* Get the get PVStructure. The record will not be processed.
|
||||
*/
|
||||
virtual void getGet() = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* epics::pvData::Requester for ChannelPutGet.
|
||||
*/
|
||||
class epicsShareClass ChannelPutGetRequester : virtual public epics::pvData::Requester
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelPutGetRequester);
|
||||
|
||||
/**
|
||||
* The client and server have both completed the createChannelPutGet request.
|
||||
* @param status Completion status.
|
||||
* @param channelPutGet The channelPutGet interface or null if the request failed.
|
||||
* @param pvPutStructure The PVStructure that holds the putData.
|
||||
* @param pvGetStructure The PVStructure that holds the getData.
|
||||
*/
|
||||
virtual void channelPutGetConnect(const epics::pvData::Status& status,ChannelPutGet::shared_pointer const & channelPutGet,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvPutStructure,epics::pvData::PVStructure::shared_pointer const & pvGetStructure) = 0;
|
||||
/**
|
||||
* The putGet request is done. This is always called with no locks held.
|
||||
* @param status Completion status.
|
||||
*/
|
||||
virtual void putGetDone(const epics::pvData::Status& status) = 0;
|
||||
|
||||
/**
|
||||
* The getPut request is done. This is always called with no locks held.
|
||||
* @param status Completion status.
|
||||
*/
|
||||
virtual void getPutDone(const epics::pvData::Status& status) = 0;
|
||||
|
||||
/**
|
||||
* The getGet request is done. This is always called with no locks held.
|
||||
* @param status Completion status.
|
||||
*/
|
||||
virtual void getGetDone(const epics::pvData::Status& status) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* epics::pvData::Requester for channelGet.
|
||||
*/
|
||||
class epicsShareClass ChannelRPC : public ChannelRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelRPC);
|
||||
|
||||
/**
|
||||
* Issue an RPC request to the channel.
|
||||
* This fails if the request can not be satisfied.
|
||||
* @param pvArgument The argument structure for an RPC request.
|
||||
* @param lastRequest Is this the last request?
|
||||
*/
|
||||
virtual void request(epics::pvData::PVStructure::shared_pointer const & pvArgument, bool lastRequest) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* epics::pvData::Requester for channelGet.
|
||||
*/
|
||||
class epicsShareClass ChannelRPCRequester : virtual public epics::pvData::Requester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelRPCRequester);
|
||||
|
||||
/**
|
||||
* The client and server have both completed the createChannelGet request.
|
||||
* @param status Completion status.
|
||||
* @param channelRPC The channelRPC interface or null if the request failed.
|
||||
*/
|
||||
virtual void channelRPCConnect(const epics::pvData::Status& status,ChannelRPC::shared_pointer const & channelRPC) = 0;
|
||||
|
||||
/**
|
||||
* The request is done. This is always called with no locks held.
|
||||
* @param status Completion status.
|
||||
* @param pvResponse The response data for the RPC request.
|
||||
*/
|
||||
virtual void requestDone(const epics::pvData::Status& status,epics::pvData::PVStructure::shared_pointer const & pvResponse) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* epics::pvData::Requester for a getStructure request.
|
||||
*/
|
||||
class epicsShareClass GetFieldRequester : virtual public epics::pvData::Requester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(GetFieldRequester);
|
||||
|
||||
/**
|
||||
* The client and server have both completed the getStructure request.
|
||||
* @param status Completion status.
|
||||
* @param field The Structure for the request.
|
||||
*/
|
||||
// TODO naming convention
|
||||
virtual void getDone(const epics::pvData::Status& status,epics::pvData::FieldConstPtr const & field) = 0;
|
||||
};
|
||||
|
||||
|
||||
class ChannelRequester;
|
||||
|
||||
/**
|
||||
* Interface for accessing a channel.
|
||||
* A channel is created via a call to ChannelAccess.createChannel(String channelName).
|
||||
*/
|
||||
class epicsShareClass Channel :
|
||||
public epics::pvData::Requester,
|
||||
public epics::pvData::Destroyable,
|
||||
private epics::pvData::NoDefaultMethods {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Channel);
|
||||
|
||||
/**
|
||||
* Channel connection status.
|
||||
*/
|
||||
enum ConnectionState {
|
||||
NEVER_CONNECTED, CONNECTED, DISCONNECTED, DESTROYED
|
||||
};
|
||||
|
||||
static const char* ConnectionStateNames[];
|
||||
|
||||
/**
|
||||
* Get the the channel provider of this channel.
|
||||
* @return The channel provider.
|
||||
*/
|
||||
virtual std::tr1::shared_ptr<ChannelProvider> getProvider() = 0;
|
||||
// virtual ChannelProvider::shared_pointer getProvider() = 0;
|
||||
|
||||
/**
|
||||
* Returns the channel's remote address, e.g. "/192.168.1.101:5064" or "#C0 S1".
|
||||
* @return the channel's remote address.
|
||||
**/
|
||||
virtual epics::pvData::String getRemoteAddress() = 0;
|
||||
|
||||
/**
|
||||
* Returns the connection state of this channel.
|
||||
* @return the <code>ConnectionState</code> value.
|
||||
**/
|
||||
virtual ConnectionState getConnectionState() = 0;
|
||||
|
||||
/**
|
||||
* Get the channel name.
|
||||
* @return The name.
|
||||
*/
|
||||
virtual epics::pvData::String getChannelName() = 0;
|
||||
|
||||
/**
|
||||
* Get the channel epics::pvData::Requester.
|
||||
* @return The epics::pvData::Requester.
|
||||
*/
|
||||
// virtual ChannelRequester::shared_pointer getChannelRequester() = 0;
|
||||
virtual std::tr1::shared_ptr<ChannelRequester> getChannelRequester() = 0;
|
||||
|
||||
/**
|
||||
* Is the channel connected?
|
||||
* @return (false,true) means (not, is) connected.
|
||||
*/
|
||||
virtual bool isConnected() = 0;
|
||||
|
||||
/**
|
||||
* Get a Field which describes the subField.
|
||||
* GetFieldRequester.getDone is called after both client and server have processed the getField request.
|
||||
* This is for clients that want to introspect a PVRecord via channel access.
|
||||
* @param epics::pvData::Requester The epics::pvData::Requester.
|
||||
* @param subField The name of the subField.
|
||||
* If this is null or an empty epics::pvData::String the returned Field is for the entire record.
|
||||
*/
|
||||
virtual void getField(GetFieldRequester::shared_pointer const & requester,epics::pvData::String const & subField) = 0;
|
||||
|
||||
/**
|
||||
* Get the access rights for a field of a PVStructure created via a call to createPVStructure.
|
||||
* MATEJ Channel access can store this info via auxInfo.
|
||||
* @param pvField The field for which access rights is desired.
|
||||
* @return The access rights.
|
||||
*/
|
||||
virtual AccessRights getAccessRights(epics::pvData::PVField::shared_pointer const & pvField) = 0;
|
||||
|
||||
/**
|
||||
* Create a ChannelProcess.
|
||||
* ChannelProcessRequester.channelProcessReady is called after both client and server are ready for
|
||||
* the client to make a process request.
|
||||
* @param channelProcessRequester The interface for notifying when this request is complete
|
||||
* and when channel completes processing.
|
||||
* @param pvRequest Additional options (e.g. triggering).
|
||||
* @return <code>ChannelProcess</code> instance.
|
||||
*/
|
||||
virtual ChannelProcess::shared_pointer createChannelProcess(
|
||||
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
|
||||
|
||||
/**
|
||||
* Create a ChannelGet.
|
||||
* ChannelGetRequester.channelGetReady is called after both client and server are ready for
|
||||
* the client to make a get request.
|
||||
* @param channelGetRequester The interface for notifying when this request is complete
|
||||
* and when a channel get completes.
|
||||
* @param pvRequest A structure describing the desired set of fields from the remote PVRecord.
|
||||
* This has the same form as a pvRequest to PVCopyFactory.create.
|
||||
* @return <code>ChannelGet</code> instance.
|
||||
*/
|
||||
virtual ChannelGet::shared_pointer createChannelGet(
|
||||
ChannelGetRequester::shared_pointer const & channelGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
|
||||
|
||||
/**
|
||||
* Create a ChannelPut.
|
||||
* ChannelPutRequester.channelPutReady is called after both client and server are ready for
|
||||
* the client to make a put request.
|
||||
* @param channelPutRequester The interface for notifying when this request is complete
|
||||
* and when a channel get completes.
|
||||
* @param pvRequest A structure describing the desired set of fields from the remote PVRecord.
|
||||
* This has the same form as a pvRequest to PVCopyFactory.create.
|
||||
* @return <code>ChannelPut</code> instance.
|
||||
*/
|
||||
virtual ChannelPut::shared_pointer createChannelPut(
|
||||
ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
|
||||
|
||||
/**
|
||||
* Create a ChannelPutGet.
|
||||
* ChannelPutGetRequester.channelPutGetReady is called after both client and server are ready for
|
||||
* the client to make a putGet request.
|
||||
* @param channelPutGetRequester The interface for notifying when this request is complete
|
||||
* and when a channel get completes.
|
||||
* @param pvRequest A structure describing the desired set of fields from the remote PVRecord.
|
||||
* This has the same form as a pvRequest to PVCopyFactory.create.
|
||||
* @return <code>ChannelPutGet</code> instance.
|
||||
*/
|
||||
virtual ChannelPutGet::shared_pointer createChannelPutGet(
|
||||
ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
|
||||
|
||||
/**
|
||||
* Create a ChannelRPC (Remote Procedure Call).
|
||||
* @param channelRPCRequester The epics::pvData::Requester.
|
||||
* @param pvRequest Request options.
|
||||
* @return <code>ChannelRPC</code> instance.
|
||||
*/
|
||||
virtual ChannelRPC::shared_pointer createChannelRPC(
|
||||
ChannelRPCRequester::shared_pointer const & channelRPCRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
|
||||
|
||||
/**
|
||||
* Create a Monitor.
|
||||
* @param monitorRequester The epics::pvData::Requester.
|
||||
* @param pvRequest A structure describing the desired set of fields from the remote PVRecord.
|
||||
* This has the same form as a pvRequest to PVCopyFactory.create.
|
||||
* @return <code>Monitor</code> instance.
|
||||
*/
|
||||
virtual epics::pvData::Monitor::shared_pointer createMonitor(
|
||||
epics::pvData::MonitorRequester::shared_pointer const & monitorRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
|
||||
|
||||
/**
|
||||
* Create a ChannelArray.
|
||||
* @param channelArrayRequester The ChannelArrayRequester
|
||||
* @param pvRequest Additional options (e.g. triggering).
|
||||
* @return <code>ChannelArray</code> instance.
|
||||
*/
|
||||
virtual ChannelArray::shared_pointer createChannelArray(
|
||||
ChannelArrayRequester::shared_pointer const & channelArrayRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest) = 0;
|
||||
|
||||
/**
|
||||
* Prints detailed information about the context to the standard output stream.
|
||||
*/
|
||||
virtual void printInfo() = 0;
|
||||
|
||||
/**
|
||||
* Prints detailed information about the context to the specified output stream.
|
||||
* @param out the output stream.
|
||||
*/
|
||||
virtual void printInfo(epics::pvData::StringBuilder out) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Listener for connect state changes.
|
||||
*/
|
||||
class epicsShareClass ChannelRequester : public virtual epics::pvData::Requester {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelRequester);
|
||||
|
||||
/**
|
||||
* A channel has been created. This may be called multiple times if there are multiple providers.
|
||||
* @param status Completion status.
|
||||
* @param channel The channel.
|
||||
*/
|
||||
virtual void channelCreated(const epics::pvData::Status& status, Channel::shared_pointer const & channel) = 0;
|
||||
|
||||
/**
|
||||
* A channel connection state change has occurred.
|
||||
* @param c The channel.
|
||||
* @param connectionState The new connection state.
|
||||
*/
|
||||
virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The FlushStrategy enum
|
||||
*/
|
||||
enum FlushStrategy {
|
||||
IMMEDIATE, DELAYED, USER_CONTROLED
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface implemented by code that can provide access to the record
|
||||
* to which a channel connects.
|
||||
*/
|
||||
class epicsShareClass ChannelProvider : public epics::pvData::Destroyable, private epics::pvData::NoDefaultMethods {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelProvider);
|
||||
|
||||
/** Minimal priority. */
|
||||
static const short PRIORITY_MIN = 0;
|
||||
/** Maximal priority. */
|
||||
static const short PRIORITY_MAX = 99;
|
||||
/** Default priority. */
|
||||
static const short PRIORITY_DEFAULT = PRIORITY_MIN;
|
||||
/** DB links priority. */
|
||||
static const short PRIORITY_LINKS_DB = PRIORITY_MAX;
|
||||
/** Archive priority. */
|
||||
static const short PRIORITY_ARCHIVE = (PRIORITY_MAX + PRIORITY_MIN) / 2;
|
||||
/** OPI priority. */
|
||||
static const short PRIORITY_OPI = PRIORITY_MIN;
|
||||
|
||||
/**
|
||||
* Get the provider name.
|
||||
* @return The name.
|
||||
*/
|
||||
virtual epics::pvData::String getProviderName() = 0;
|
||||
|
||||
/**
|
||||
* Find a channel.
|
||||
* @param channelName The channel name.
|
||||
* @param channelFindRequester The epics::pvData::Requester.
|
||||
* @return An interface for the find.
|
||||
*/
|
||||
virtual ChannelFind::shared_pointer channelFind(epics::pvData::String const & channelName,
|
||||
ChannelFindRequester::shared_pointer const & channelFindRequester) = 0;
|
||||
|
||||
/**
|
||||
* Create a channel.
|
||||
* @param channelName The name of the channel.
|
||||
* @param channelRequester The epics::pvData::Requester.
|
||||
* @param priority channel priority, must be <code>PRIORITY_MIN</code> <= priority <= <code>PRIORITY_MAX</code>.
|
||||
* @return <code>Channel</code> instance. If channel does not exist <code>null</code> is returned and <code>channelRequester</code> notified.
|
||||
*/
|
||||
virtual Channel::shared_pointer createChannel(epics::pvData::String const & channelName,ChannelRequester::shared_pointer const & channelRequester,
|
||||
short priority = PRIORITY_DEFAULT) = 0;
|
||||
|
||||
/**
|
||||
* Create a channel.
|
||||
* @param channelName The name of the channel.
|
||||
* @param channelRequester The epics::pvData::Requester.
|
||||
* @param priority channel priority, must be <code>PRIORITY_MIN</code> <= priority <= <code>PRIORITY_MAX</code>.
|
||||
* @param address address (or list of addresses) where to look for a channel. Implementation independed epics::pvData::String.
|
||||
* @return <code>Channel</code> instance. If channel does not exist <code>null</code> is returned and <code>channelRequester</code> notified.
|
||||
*/
|
||||
virtual Channel::shared_pointer createChannel(epics::pvData::String const & channelName,ChannelRequester::shared_pointer const & channelRequester,
|
||||
short priority, epics::pvData::String const & address) = 0;
|
||||
|
||||
virtual void configure(epics::pvData::PVStructure::shared_pointer /*configuration*/) {};
|
||||
virtual void flush() {};
|
||||
virtual void poll() {};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* <code>ChanneProvider</code> factory interface.
|
||||
*/
|
||||
class epicsShareClass ChannelProviderFactory : private epics::pvData::NoDefaultMethods {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelProviderFactory);
|
||||
|
||||
virtual ~ChannelProviderFactory() {};
|
||||
|
||||
/**
|
||||
* Get factory name (i.e. name of the provider).
|
||||
* @return the factory name.
|
||||
*/
|
||||
virtual epics::pvData::String getFactoryName() = 0;
|
||||
|
||||
/**
|
||||
* Get a shared instance.
|
||||
* @return a shared instance.
|
||||
*/
|
||||
virtual ChannelProvider::shared_pointer sharedInstance() = 0;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @return a new instance.
|
||||
*/
|
||||
virtual ChannelProvider::shared_pointer newInstance() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for locating channel providers.
|
||||
*/
|
||||
class epicsShareClass ChannelAccess : private epics::pvData::NoDefaultMethods {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelAccess);
|
||||
|
||||
typedef std::vector<epics::pvData::String> stringVector_t;
|
||||
|
||||
virtual ~ChannelAccess() {};
|
||||
|
||||
/**
|
||||
* Get a shared instance of the provider with the specified name.
|
||||
* @param providerName The name of the provider.
|
||||
* @return The interface for the provider or null if the provider is not known.
|
||||
*/
|
||||
virtual ChannelProvider::shared_pointer getProvider(epics::pvData::String const & providerName) = 0;
|
||||
|
||||
/**
|
||||
* Creates a new instanceof the provider with the specified name.
|
||||
* @param providerName The name of the provider.
|
||||
* @return The interface for the provider or null if the provider is not known.
|
||||
*/
|
||||
virtual ChannelProvider::shared_pointer createProvider(epics::pvData::String const & providerName) = 0;
|
||||
|
||||
/**
|
||||
* Get a array of the names of all the known providers.
|
||||
* @return The names. Be sure to delete vector instance.
|
||||
*/
|
||||
virtual std::auto_ptr<stringVector_t> getProviderNames() = 0;
|
||||
};
|
||||
|
||||
epicsShareExtern ChannelAccess::shared_pointer getChannelAccess();
|
||||
epicsShareExtern void registerChannelProviderFactory(ChannelProviderFactory::shared_pointer const & channelProviderFactory);
|
||||
epicsShareExtern void unregisterChannelProviderFactory(ChannelProviderFactory::shared_pointer const & channelProviderFactory);
|
||||
|
||||
/**
|
||||
* Interface for creating request structure.
|
||||
*/
|
||||
class epicsShareClass CreateRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(CreateRequest);
|
||||
static CreateRequest::shared_pointer create();
|
||||
virtual ~CreateRequest() {};
|
||||
|
||||
/**
|
||||
* Create a request structure for the create calls in Channel.
|
||||
* See the package overview documentation for details.
|
||||
* @param request The field request. See the package overview documentation for details.
|
||||
* @param requester The requester;
|
||||
* @return The request PVStructure if a valid request was given.
|
||||
* If a NULL PVStructure is returned then getMessage will return
|
||||
* the reason.
|
||||
*/
|
||||
virtual epics::pvData::PVStructure::shared_pointer createRequest(
|
||||
epics::pvData::String const & request) = 0;
|
||||
/**
|
||||
* Get the error message of createRequest returns NULL;
|
||||
* @return the error message
|
||||
*/
|
||||
epics::pvData::String getMessage() {return message;}
|
||||
protected:
|
||||
CreateRequest() {}
|
||||
epics::pvData::String message;
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* PVACCESS_H */
|
||||
92
src/factory/ChannelAccessFactory.cpp
Normal file
92
src/factory/ChannelAccessFactory.cpp
Normal file
@@ -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 <map>
|
||||
#include <vector>
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/noDefaultMethods.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/factory.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
static ChannelAccess::shared_pointer channelAccess;
|
||||
|
||||
static Mutex channelProviderMutex;
|
||||
|
||||
typedef std::map<String, ChannelProviderFactory::shared_pointer> ChannelProviderFactoryMap;
|
||||
static ChannelProviderFactoryMap channelProviders;
|
||||
|
||||
|
||||
class ChannelAccessImpl : public ChannelAccess {
|
||||
public:
|
||||
|
||||
ChannelProvider::shared_pointer getProvider(String const & _providerName) {
|
||||
|
||||
// TODO remove, here for backward compatibility
|
||||
const String providerName = (_providerName == "pvAccess") ? "pva" : _providerName;
|
||||
|
||||
Lock guard(channelProviderMutex);
|
||||
ChannelProviderFactoryMap::const_iterator iter = channelProviders.find(providerName);
|
||||
if (iter != channelProviders.end())
|
||||
return iter->second->sharedInstance();
|
||||
else
|
||||
return ChannelProvider::shared_pointer();
|
||||
}
|
||||
|
||||
ChannelProvider::shared_pointer createProvider(String const & _providerName) {
|
||||
|
||||
// TODO remove, here for backward compatibility
|
||||
const String providerName = (_providerName == "pvAccess") ? "pva" : _providerName;
|
||||
|
||||
Lock guard(channelProviderMutex);
|
||||
ChannelProviderFactoryMap::const_iterator iter = channelProviders.find(providerName);
|
||||
if (iter != channelProviders.end())
|
||||
return iter->second->newInstance();
|
||||
else
|
||||
return ChannelProvider::shared_pointer();
|
||||
}
|
||||
|
||||
std::auto_ptr<stringVector_t> getProviderNames() {
|
||||
Lock guard(channelProviderMutex);
|
||||
std::auto_ptr<stringVector_t> providers(new stringVector_t());
|
||||
for (ChannelProviderFactoryMap::const_iterator i = channelProviders.begin();
|
||||
i != channelProviders.end(); i++)
|
||||
providers->push_back(i->first);
|
||||
|
||||
return providers;
|
||||
}
|
||||
};
|
||||
|
||||
ChannelAccess::shared_pointer getChannelAccess() {
|
||||
static Mutex mutex;
|
||||
Lock guard(mutex);
|
||||
|
||||
if(channelAccess.get()==0){
|
||||
channelAccess.reset(new ChannelAccessImpl());
|
||||
}
|
||||
return channelAccess;
|
||||
}
|
||||
|
||||
void registerChannelProviderFactory(ChannelProviderFactory::shared_pointer const & channelProviderFactory) {
|
||||
Lock guard(channelProviderMutex);
|
||||
channelProviders[channelProviderFactory->getFactoryName()] = channelProviderFactory;
|
||||
}
|
||||
|
||||
void unregisterChannelProviderFactory(ChannelProviderFactory::shared_pointer const & channelProviderFactory) {
|
||||
Lock guard(channelProviderMutex);
|
||||
channelProviders.erase(channelProviderFactory->getFactoryName());
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
302
src/factory/CreateRequestFactory.cpp
Normal file
302
src/factory/CreateRequestFactory.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* 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 <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/lock.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
using std::tr1::static_pointer_cast;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
static PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
|
||||
class CreateRequestImpl : public CreateRequest {
|
||||
private:
|
||||
|
||||
static void removeBlanks(String& str)
|
||||
{
|
||||
while(true) {
|
||||
String::size_type pos = str.find_first_of(' ');
|
||||
if(pos==String::npos) return;
|
||||
str.erase(pos,1);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t findMatchingBrace(String& request, size_t index, int numOpen) {
|
||||
size_t openBrace = request.find('{', index+1);
|
||||
size_t closeBrace = request.find('}', index+1);
|
||||
if(openBrace == String::npos && closeBrace == std::string::npos) return std::string::npos;
|
||||
if (openBrace != String::npos && openBrace!=0) {
|
||||
if(openBrace<closeBrace) return findMatchingBrace(request,openBrace,numOpen+1);
|
||||
if(numOpen==1) return closeBrace;
|
||||
return findMatchingBrace(request,closeBrace,numOpen-1);
|
||||
}
|
||||
if(numOpen==1) return closeBrace;
|
||||
return findMatchingBrace(request,closeBrace,numOpen-1);
|
||||
}
|
||||
|
||||
static std::vector<String> split(String const & commaSeparatedList) {
|
||||
String::size_type numValues = 1;
|
||||
String::size_type index=0;
|
||||
while(true) {
|
||||
String::size_type pos = commaSeparatedList.find(',',index);
|
||||
if(pos==String::npos) break;
|
||||
numValues++;
|
||||
index = pos +1;
|
||||
}
|
||||
std::vector<String> valueList(numValues,"");
|
||||
index=0;
|
||||
for(size_t i=0; i<numValues; i++) {
|
||||
size_t pos = commaSeparatedList.find(',',index);
|
||||
String value = commaSeparatedList.substr(index,pos-index);
|
||||
valueList[i] = value;
|
||||
index = pos +1;
|
||||
}
|
||||
return valueList;
|
||||
}
|
||||
|
||||
|
||||
bool createRequestOptions(
|
||||
PVStructurePtr const & pvParent,
|
||||
String request)
|
||||
{
|
||||
removeBlanks(request);
|
||||
if(request.length()<=1) return true;
|
||||
std::vector<String> items = split(request);
|
||||
size_t nitems = items.size();
|
||||
StringArray fieldNames;
|
||||
PVFieldPtrArray pvFields;
|
||||
fieldNames.reserve(nitems);
|
||||
pvFields.reserve(nitems);
|
||||
for(size_t j=0; j<nitems; j++) {
|
||||
String item = items[j];
|
||||
size_t equals = item.find('=');
|
||||
if(equals==String::npos || equals==0) {
|
||||
message = item + " illegal option";
|
||||
return false;
|
||||
}
|
||||
String name = item.substr(0,equals);
|
||||
String value = item.substr(equals+1);
|
||||
fieldNames.push_back(name);
|
||||
PVStringPtr pvValue = static_pointer_cast<PVString>(getPVDataCreate()->createPVScalar(pvString));
|
||||
pvValue->put(value);
|
||||
pvFields.push_back(pvValue);
|
||||
}
|
||||
PVStructurePtr pvOptions = getPVDataCreate()->createPVStructure(fieldNames,pvFields);
|
||||
pvParent->appendPVField("_options",pvOptions);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool createFieldRequest(
|
||||
PVStructurePtr const & pvParent,
|
||||
String request)
|
||||
{
|
||||
static PVFieldPtrArray emptyFields;
|
||||
static StringArray emptyFieldNames;
|
||||
|
||||
removeBlanks(request);
|
||||
if(request.length()<=0) return true;
|
||||
size_t comma = request.find(',');
|
||||
if(comma==0) {
|
||||
return createFieldRequest(pvParent,request.substr(1));
|
||||
}
|
||||
size_t openBrace = request.find('{');
|
||||
size_t openBracket = request.find('[');
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(emptyFieldNames, emptyFields);
|
||||
if(comma==String::npos && openBrace==std::string::npos && openBracket==std::string::npos) {
|
||||
size_t period = request.find('.');
|
||||
if(period!=String::npos && period!=0) {
|
||||
String fieldName = request.substr(0,period);
|
||||
request = request.substr(period+1);
|
||||
pvParent->appendPVField(fieldName, pvStructure);
|
||||
return createFieldRequest(pvStructure,request);
|
||||
}
|
||||
pvParent->appendPVField(request, pvStructure);
|
||||
return true;
|
||||
}
|
||||
size_t end = comma;
|
||||
if(openBrace!=String::npos && (end>openBrace || end==std::string::npos)) end = openBrace;
|
||||
if(openBracket!=String::npos && (end>openBracket || end==std::string::npos)) end = openBracket;
|
||||
String nextFieldName = request.substr(0,end);
|
||||
if(end==comma) {
|
||||
size_t period = nextFieldName.find('.');
|
||||
if(period!=String::npos && period!=0) {
|
||||
String fieldName = nextFieldName.substr(0,period);
|
||||
PVStructurePtr xxx= pvDataCreate->createPVStructure(emptyFieldNames, emptyFields);
|
||||
String rest = nextFieldName.substr(period+1);
|
||||
createFieldRequest(xxx,rest);
|
||||
pvParent->appendPVField(fieldName, xxx);
|
||||
} else {
|
||||
pvParent->appendPVField(nextFieldName, pvStructure);
|
||||
}
|
||||
request = request.substr(end+1);
|
||||
return createFieldRequest(pvParent,request);
|
||||
}
|
||||
if(end==openBracket) {
|
||||
size_t closeBracket = request.find(']');
|
||||
if(closeBracket==String::npos || closeBracket==0) {
|
||||
message = request + " does not have matching ]";
|
||||
return false;
|
||||
}
|
||||
String options = request.substr(openBracket+1, closeBracket-openBracket-1);
|
||||
size_t period = nextFieldName.find('.');
|
||||
if(period!=String::npos && period!=0) {
|
||||
String fieldName = nextFieldName.substr(0,period);
|
||||
PVStructurePtr xxx = pvDataCreate->createPVStructure(emptyFieldNames, emptyFields);
|
||||
if(!createRequestOptions(xxx,options)) return false;
|
||||
String rest = nextFieldName.substr(period+1);
|
||||
createFieldRequest(xxx,rest);
|
||||
pvParent->appendPVField(fieldName, xxx);
|
||||
} else {
|
||||
if(!createRequestOptions(pvStructure,options)) return false;
|
||||
pvParent->appendPVField(nextFieldName, pvStructure);
|
||||
}
|
||||
request = request.substr(end+1);
|
||||
return createFieldRequest(pvParent,request);
|
||||
}
|
||||
// end== openBrace
|
||||
size_t closeBrace = findMatchingBrace(request,openBrace+1,1);
|
||||
if(closeBrace==String::npos || closeBrace==0) {
|
||||
message = request + " does not have matching }";
|
||||
return false;
|
||||
}
|
||||
String subFields = request.substr(openBrace+1, closeBrace-openBrace-1);
|
||||
if(!createFieldRequest(pvStructure,subFields)) return false;
|
||||
request = request.substr(closeBrace+1);
|
||||
size_t period = nextFieldName.find('.');
|
||||
if(period==String::npos) {
|
||||
pvParent->appendPVField(nextFieldName,pvStructure);
|
||||
return createFieldRequest(pvParent,request);
|
||||
}
|
||||
PVStructure::shared_pointer yyy = pvParent;
|
||||
while(period!=String::npos && period!=0) {
|
||||
String fieldName = nextFieldName.substr(0,period);
|
||||
PVStructurePtr xxx = pvDataCreate->createPVStructure(emptyFieldNames, emptyFields);
|
||||
yyy->appendPVField(fieldName,xxx);
|
||||
nextFieldName = nextFieldName.substr(period+1);
|
||||
period = nextFieldName.find('.');
|
||||
if(period==String::npos || period==0) {
|
||||
xxx->appendPVField(nextFieldName, pvStructure);
|
||||
break;
|
||||
}
|
||||
yyy = xxx;
|
||||
}
|
||||
return createFieldRequest(pvParent,request);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
virtual PVStructure::shared_pointer createRequest(
|
||||
String const & crequest)
|
||||
{
|
||||
String request = crequest;
|
||||
PVFieldPtrArray pvFields;
|
||||
StringArray fieldNames;
|
||||
PVStructurePtr emptyPVStructure = pvDataCreate->createPVStructure(fieldNames,pvFields);
|
||||
static PVStructure::shared_pointer nullStructure;
|
||||
|
||||
if (!request.empty()) removeBlanks(request);
|
||||
if (request.empty())
|
||||
{
|
||||
return emptyPVStructure;
|
||||
}
|
||||
size_t offsetRecord = request.find("record[");
|
||||
size_t offsetField = request.find("field(");
|
||||
size_t offsetPutField = request.find("putField(");
|
||||
size_t offsetGetField = request.find("getField(");
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(emptyPVStructure);
|
||||
if (offsetRecord != String::npos) {
|
||||
size_t offsetBegin = request.find('[', offsetRecord);
|
||||
size_t offsetEnd = request.find(']', offsetBegin);
|
||||
if(offsetEnd == String::npos) {
|
||||
message = request.substr(offsetRecord)
|
||||
+ " record[ does not have matching ]";
|
||||
return nullStructure;
|
||||
}
|
||||
PVStructurePtr pvStruct = pvDataCreate->createPVStructure(emptyPVStructure);
|
||||
if(!createRequestOptions(
|
||||
pvStruct,request.substr(offsetBegin+1,
|
||||
offsetEnd-offsetBegin-1)))
|
||||
{
|
||||
return nullStructure;
|
||||
}
|
||||
pvStructure->appendPVField("record", pvStruct);
|
||||
}
|
||||
if (offsetField != String::npos) {
|
||||
size_t offsetBegin = request.find('(', offsetField);
|
||||
size_t offsetEnd = request.find(')', offsetBegin);
|
||||
if(offsetEnd == String::npos) {
|
||||
message = request.substr(offsetField)
|
||||
+ " field( does not have matching )";
|
||||
return nullStructure;
|
||||
}
|
||||
PVStructurePtr pvStruct = pvDataCreate->createPVStructure(emptyPVStructure);
|
||||
if(!createFieldRequest(
|
||||
pvStruct,request.substr(offsetBegin+1,
|
||||
offsetEnd-offsetBegin-1)))
|
||||
{
|
||||
return nullStructure;
|
||||
}
|
||||
pvStructure->appendPVField("field", pvStruct);
|
||||
}
|
||||
if (offsetPutField != String::npos) {
|
||||
size_t offsetBegin = request.find('(', offsetPutField);
|
||||
size_t offsetEnd = request.find(')', offsetBegin);
|
||||
if(offsetEnd == String::npos) {
|
||||
message = request.substr(offsetField)
|
||||
+ " putField( does not have matching )";
|
||||
return nullStructure;
|
||||
}
|
||||
PVStructurePtr pvStruct = pvDataCreate->createPVStructure(emptyPVStructure);
|
||||
if(!createFieldRequest(
|
||||
pvStruct,request.substr(offsetBegin+1,
|
||||
offsetEnd-offsetBegin-1)))
|
||||
{
|
||||
return nullStructure;
|
||||
}
|
||||
pvStructure->appendPVField("putField", pvStruct);
|
||||
}
|
||||
if (offsetGetField != String::npos) {
|
||||
size_t offsetBegin = request.find('(', offsetGetField);
|
||||
size_t offsetEnd = request.find(')', offsetBegin);
|
||||
if(offsetEnd == String::npos) {
|
||||
message = request.substr(offsetField)
|
||||
+ " getField( does not have matching )";
|
||||
return nullStructure;
|
||||
}
|
||||
PVStructurePtr pvStruct = pvDataCreate->createPVStructure(emptyPVStructure);
|
||||
if(!createFieldRequest(
|
||||
pvStruct,request.substr(offsetBegin+1,
|
||||
offsetEnd-offsetBegin-1)))
|
||||
{
|
||||
return nullStructure;
|
||||
}
|
||||
pvStructure->appendPVField("getField", pvStruct);
|
||||
}
|
||||
if (pvStructure.get()->getStructure()->getNumberFields()==0) {
|
||||
if(!createFieldRequest(pvStructure,request)) return nullStructure;
|
||||
}
|
||||
return pvStructure;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CreateRequest::shared_pointer CreateRequest::create()
|
||||
{
|
||||
CreateRequest::shared_pointer createRequest(new CreateRequestImpl());
|
||||
return createRequest;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
6
src/factory/Makefile
Normal file
6
src/factory/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/factory
|
||||
|
||||
LIBSRCS += ChannelAccessFactory.cpp
|
||||
LIBSRCS += CreateRequestFactory.cpp
|
||||
7
src/mb/Makefile
Normal file
7
src/mb/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/mb
|
||||
|
||||
INC += pvAccessMB.h
|
||||
|
||||
LIBSRCS += pvAccessMB.cpp
|
||||
3
src/mb/pvAccessMB.cpp
Normal file
3
src/mb/pvAccessMB.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "pvAccessMB.h"
|
||||
|
||||
MB_DECLARE(channelGet, 100000);
|
||||
18
src/mb/pvAccessMB.h
Normal file
18
src/mb/pvAccessMB.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _PVACCESSMB_H_
|
||||
#define _PVACCESSMB_H_
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define pvAccessMBEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/mb.h>
|
||||
|
||||
#ifdef pvAccessMBEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef pvAccessMBEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
MB_DECLARE_EXTERN(channelGet);
|
||||
|
||||
#endif
|
||||
10
src/pva/Makefile
Normal file
10
src/pva/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/pva
|
||||
|
||||
INC += pvaConstants.h
|
||||
INC += pvaVersion.h
|
||||
INC += clientFactory.h
|
||||
|
||||
LIBSRCS += pvaVersion.cpp
|
||||
LIBSRCS += clientFactory.cpp
|
||||
101
src/pva/clientFactory.cpp
Normal file
101
src/pva/clientFactory.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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/lock.h>
|
||||
#include <pv/logger.h>
|
||||
|
||||
#include <epicsSignal.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/clientFactory.h>
|
||||
#include <pv/clientContextImpl.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
|
||||
// TODO global static variable (de/initialization order not guaranteed)
|
||||
static Mutex mutex;
|
||||
static ClientContextImpl::shared_pointer context;
|
||||
|
||||
class ChannelProviderFactoryImpl : public ChannelProviderFactory
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelProviderFactoryImpl);
|
||||
|
||||
virtual epics::pvData::String getFactoryName()
|
||||
{
|
||||
return ClientContextImpl::PROVIDER_NAME;
|
||||
}
|
||||
|
||||
virtual ChannelProvider::shared_pointer sharedInstance()
|
||||
{
|
||||
Lock guard(mutex);
|
||||
if (!context.get())
|
||||
{
|
||||
try {
|
||||
ClientContextImpl::shared_pointer lcontext = createClientContextImpl();
|
||||
lcontext->initialize();
|
||||
context = lcontext;
|
||||
} catch (std::exception &e) {
|
||||
LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what());
|
||||
} catch (...) {
|
||||
LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
return context->getProvider();
|
||||
}
|
||||
|
||||
virtual ChannelProvider::shared_pointer newInstance()
|
||||
{
|
||||
Lock guard(mutex);
|
||||
try {
|
||||
ClientContextImpl::shared_pointer lcontext = createClientContextImpl();
|
||||
lcontext->initialize();
|
||||
return lcontext->getProvider();
|
||||
} catch (std::exception &e) {
|
||||
LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what());
|
||||
return ChannelProvider::shared_pointer();
|
||||
} catch (...) {
|
||||
LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__);
|
||||
return ChannelProvider::shared_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
void destroySharedInstance()
|
||||
{
|
||||
Lock guard(mutex);
|
||||
if (context.get())
|
||||
{
|
||||
context->dispose();
|
||||
context.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static ChannelProviderFactoryImpl::shared_pointer factory;
|
||||
|
||||
void ClientFactory::start()
|
||||
{
|
||||
epicsSignalInstallSigAlarmIgnore();
|
||||
epicsSignalInstallSigPipeIgnore();
|
||||
|
||||
Lock guard(mutex);
|
||||
if (!factory.get())
|
||||
factory.reset(new ChannelProviderFactoryImpl());
|
||||
|
||||
registerChannelProviderFactory(factory);
|
||||
}
|
||||
|
||||
void ClientFactory::stop()
|
||||
{
|
||||
Lock guard(mutex);
|
||||
|
||||
if (factory.get())
|
||||
{
|
||||
unregisterChannelProviderFactory(factory);
|
||||
factory->destroySharedInstance();
|
||||
}
|
||||
}
|
||||
23
src/pva/clientFactory.h
Normal file
23
src/pva/clientFactory.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CLIENTFACTORY_H
|
||||
#define CLIENTFACTORY_H
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class epicsShareClass ClientFactory {
|
||||
public:
|
||||
static void start();
|
||||
static void stop();
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* CLIENTFACTORY_H */
|
||||
89
src/pva/pvaConstants.h
Normal file
89
src/pva/pvaConstants.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef PVACONSTANTS_H_
|
||||
#define PVACONSTANTS_H_
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define pvaConstantsepicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvType.h>
|
||||
|
||||
#ifdef pvaConstantsepicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef pvaConstantsepicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
/** PVA protocol magic number */
|
||||
const epics::pvData::int8 PVA_MAGIC = static_cast<epics::pvData::int8>(0xCA);
|
||||
|
||||
/** PVA protocol revision (implemented by this library). */
|
||||
const epics::pvData::int8 PVA_PROTOCOL_REVISION = 0;
|
||||
|
||||
/** PVA version signature used to report this implementation version in header. */
|
||||
const epics::pvData::int8 PVA_VERSION = PVA_PROTOCOL_REVISION;
|
||||
|
||||
/** Default PVA server port. */
|
||||
const epics::pvData::int32 PVA_SERVER_PORT = 5075;
|
||||
|
||||
/** Default PVA beacon port. */
|
||||
const epics::pvData::int32 PVA_BROADCAST_PORT = 5076;
|
||||
|
||||
/** PVA protocol message header size. */
|
||||
const epics::pvData::int16 PVA_MESSAGE_HEADER_SIZE = 8;
|
||||
|
||||
/** All messages must be aligned to 8-bytes (64-bit). */
|
||||
const epics::pvData::int32 PVA_ALIGNMENT = 1; // TODO
|
||||
|
||||
/**
|
||||
* UDP maximum send message size.
|
||||
* MAX_UDP: 1500 (max of ethernet and 802.{2,3} MTU) - 20/40(IPv4/IPv6)
|
||||
* - 8(UDP) - some reserve (the MTU of Ethernet is currently independent
|
||||
* of its speed variant)
|
||||
*/
|
||||
const epics::pvData::int32 MAX_UDP_UNFRAGMENTED_SEND = 1440;
|
||||
|
||||
/**
|
||||
* UDP maximum receive message size.
|
||||
* MAX_UDP: 65535 (max UDP packet size) - 20/40(IPv4/IPv6) - 8(UDP)
|
||||
*/
|
||||
const epics::pvData::int32 MAX_UDP_RECV = 65487;
|
||||
|
||||
/** TCP maximum receive message size. */
|
||||
const epics::pvData::int32 MAX_TCP_RECV = 1024*16;
|
||||
|
||||
/** Maximum number of search requests in one search message. */
|
||||
const epics::pvData::int32 MAX_SEARCH_BATCH_COUNT = 0x7FFF; // 32767
|
||||
|
||||
/** Default priority (corresponds to POSIX SCHED_OTHER) */
|
||||
const epics::pvData::int16 PVA_DEFAULT_PRIORITY = 0;
|
||||
|
||||
/** Unreasonable channel name length. */
|
||||
const epics::pvData::uint32 MAX_CHANNEL_NAME_LENGTH = 500;
|
||||
|
||||
/** Invalid data type. */
|
||||
const epics::pvData::int16 INVALID_DATA_TYPE = static_cast<epics::pvData::int16>(0xFFFF);
|
||||
|
||||
/** Invalid IOID. */
|
||||
const epics::pvData::int32 INVALID_IOID = 0;
|
||||
|
||||
/** Default PVA provider name. */
|
||||
const epics::pvData::String PVACCESS_DEFAULT_PROVIDER = "local";
|
||||
|
||||
/** "All-providers registered" PVA provider name. */
|
||||
const epics::pvData::String PVACCESS_ALL_PROVIDERS = "<all>";
|
||||
|
||||
/** Name of the system env. variable to turn on debugging. */
|
||||
const epics::pvData::String PVACCESS_DEBUG = "EPICS_PVA_DEBUG";
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* PVACONSTANTS_H_ */
|
||||
69
src/pva/pvaVersion.cpp
Normal file
69
src/pva/pvaVersion.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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 <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvaVersion.h>
|
||||
|
||||
using std::stringstream;
|
||||
using epics::pvData::String;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
Version::Version(epics::pvData::String const & productName,
|
||||
epics::pvData::String const & implementationLangugage,
|
||||
int majorVersion, int minorVersion,
|
||||
int maintenanceVersion, bool developmentFlag) :
|
||||
_productName(productName),
|
||||
_implementationLanguage(implementationLangugage),
|
||||
_majorVersion(majorVersion),
|
||||
_minorVersion(minorVersion),
|
||||
_maintenanceVersion(maintenanceVersion),
|
||||
_developmentFlag(developmentFlag)
|
||||
{
|
||||
}
|
||||
|
||||
const epics::pvData::String Version::getProductName() const {
|
||||
return _productName;
|
||||
}
|
||||
|
||||
const epics::pvData::String Version::getImplementationLanguage() const {
|
||||
return _implementationLanguage;
|
||||
}
|
||||
|
||||
int Version::getMajorVersion() const {
|
||||
return _majorVersion;
|
||||
}
|
||||
|
||||
int Version::getMinorVersion() const {
|
||||
return _minorVersion;
|
||||
}
|
||||
|
||||
int Version::getMaintenanceVersion() const {
|
||||
return _maintenanceVersion;
|
||||
}
|
||||
|
||||
bool Version::isDevelopmentVersion() const {
|
||||
return _developmentFlag;
|
||||
}
|
||||
|
||||
const String Version::getVersionString() const {
|
||||
stringstream ret;
|
||||
ret<<getProductName()<<" v"<<getMajorVersion()<<"."<<getMinorVersion()<<'.'<<getMaintenanceVersion();
|
||||
if (isDevelopmentVersion())
|
||||
ret<<"-SNAPSHOT";
|
||||
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const Version& v) {
|
||||
return o << v.getVersionString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
123
src/pva/pvaVersion.h
Normal file
123
src/pva/pvaVersion.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef VERSION_H_
|
||||
#define VERSION_H_
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define pvaVersionEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/noDefaultMethods.h>
|
||||
|
||||
#ifdef pvaVersionEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef pvaVersionEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
// module version
|
||||
// TODO to be generated, etc.
|
||||
#define EPICS_PVA_MAJOR_VERSION 3
|
||||
#define EPICS_PVA_MINOR_VERSION 0
|
||||
#define EPICS_PVA_MAINTENANCE_VERSION 5
|
||||
#define EPICS_PVA_DEVELOPMENT_FLAG 1
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class epicsShareClass Version : public epics::pvData::NoDefaultMethods {
|
||||
public:
|
||||
/**
|
||||
* Default constructor.
|
||||
* @param productName product name.
|
||||
* @param implementationLangugage implementation language.
|
||||
* @param majorVersion major version.
|
||||
* @param minorVersion minor version.
|
||||
* @param maintenanceVersion maintenance version.
|
||||
* @param developmentFlag development indicator flag.
|
||||
*/
|
||||
Version(epics::pvData::String const & productName,
|
||||
epics::pvData::String const & implementationLangugage,
|
||||
int majorVersion, int minorVersion,
|
||||
int maintenanceVersion, bool developmentFlag);
|
||||
|
||||
/** The name of the product */
|
||||
const epics::pvData::String getProductName() const;
|
||||
|
||||
/** Implementation Language: C++
|
||||
*/
|
||||
const epics::pvData::String getImplementationLanguage() const;
|
||||
|
||||
/**
|
||||
* Major version number. This changes only when there is a
|
||||
* significant, externally apparent enhancement from the previous release.
|
||||
* 'n' represents the n'th version.
|
||||
*
|
||||
* Clients should carefully consider the implications of new versions as
|
||||
* external interfaces and behaviour may have changed.
|
||||
*/
|
||||
int getMajorVersion() const;
|
||||
|
||||
/**
|
||||
* Minor version number. This changes when:
|
||||
* <ul>
|
||||
* <li>a new set of functionality is to be added</li>
|
||||
* <li>API or behaviour change</li>
|
||||
* <li>its designated as a reference release</li>
|
||||
* </ul>
|
||||
*/
|
||||
int getMinorVersion() const;
|
||||
|
||||
/**
|
||||
* Maintenance version number. Optional identifier used to designate
|
||||
* maintenance drop applied to a specific release and contains fixes for
|
||||
* defects reported. It maintains compatibility with the release and
|
||||
* contains no API changes. When missing, it designates the final and
|
||||
* complete development drop for a release.
|
||||
*/
|
||||
int getMaintenanceVersion() const;
|
||||
|
||||
/**
|
||||
* Development flag.
|
||||
*
|
||||
* Development drops are works in progress towards a completed, final
|
||||
* release. A specific development drop may not completely implement all
|
||||
* aspects of a new feature, which may take several development drops to
|
||||
* complete. At the point of the final drop for the release, the -SNAPSHOT suffix
|
||||
* will be omitted.
|
||||
*/
|
||||
bool isDevelopmentVersion() const;
|
||||
|
||||
/**
|
||||
* Get the long version string.
|
||||
* @return epics::pvData::String denoting current version
|
||||
*/
|
||||
const epics::pvData::String getLongVersionString() const;
|
||||
|
||||
/**
|
||||
* Get the basic version string.
|
||||
* @return epics::pvData::String denoting current version
|
||||
*/
|
||||
const epics::pvData::String getVersionString() const;
|
||||
|
||||
private:
|
||||
epics::pvData::String _productName;
|
||||
epics::pvData::String _implementationLanguage;
|
||||
int _majorVersion;
|
||||
int _minorVersion;
|
||||
int _maintenanceVersion;
|
||||
bool _developmentFlag;
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Version& v);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* VERSION_H_ */
|
||||
24
src/remote/Makefile
Normal file
24
src/remote/Makefile
Normal file
@@ -0,0 +1,24 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/remote
|
||||
|
||||
INC += remote.h
|
||||
INC += blockingUDP.h
|
||||
INC += beaconHandler.h
|
||||
INC += blockingTCP.h
|
||||
INC += channelSearchManager.h
|
||||
INC += simpleChannelSearchManagerImpl.h
|
||||
INC += transportRegistry.h
|
||||
INC += serializationHelper.h
|
||||
INC += codec.h
|
||||
|
||||
LIBSRCS += blockingUDPTransport.cpp
|
||||
LIBSRCS += blockingUDPConnector.cpp
|
||||
LIBSRCS += beaconHandler.cpp
|
||||
LIBSRCS += blockingTCPConnector.cpp
|
||||
LIBSRCS += simpleChannelSearchManagerImpl.cpp
|
||||
LIBSRCS += abstractResponseHandler.cpp
|
||||
LIBSRCS += blockingTCPAcceptor.cpp
|
||||
LIBSRCS += transportRegistry.cpp
|
||||
LIBSRCS += serializationHelper.cpp
|
||||
LIBSRCS += codec.cpp
|
||||
41
src/remote/abstractResponseHandler.cpp
Normal file
41
src/remote/abstractResponseHandler.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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/remote.h>
|
||||
#include <pv/hexDump.h>
|
||||
|
||||
#include <pv/byteBuffer.h>
|
||||
|
||||
#include <osiSock.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using std::ostringstream;
|
||||
using std::hex;
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
void AbstractResponseHandler::handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & /*transport*/, int8 version, int8 command,
|
||||
size_t payloadSize, ByteBuffer* payloadBuffer) {
|
||||
if(_debug) {
|
||||
char ipAddrStr[48];
|
||||
ipAddrToDottedIP(&responseFrom->ia, ipAddrStr, sizeof(ipAddrStr));
|
||||
|
||||
ostringstream prologue;
|
||||
prologue<<"Message [0x"<<hex<<(int)command<<", v0x"<<hex;
|
||||
prologue<<(int)version<<"] received from "<<ipAddrStr;
|
||||
|
||||
hexDump(prologue.str(), _description,
|
||||
(const int8*)payloadBuffer->getArray(),
|
||||
payloadBuffer->getPosition(), static_cast<int>(payloadSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
96
src/remote/beaconHandler.cpp
Normal file
96
src/remote/beaconHandler.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 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/beaconHandler.h>
|
||||
#include <pv/transportRegistry.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
BeaconHandler::BeaconHandler(Context::shared_pointer const & context,
|
||||
const osiSockAddr* responseFrom) :
|
||||
_context(Context::weak_pointer(context)),
|
||||
_responseFrom(*responseFrom),
|
||||
_mutex(),
|
||||
_serverStartupTime(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BeaconHandler::BeaconHandler(const osiSockAddr* responseFrom) :
|
||||
_responseFrom(*responseFrom),
|
||||
_mutex(),
|
||||
_serverStartupTime(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BeaconHandler::~BeaconHandler()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void BeaconHandler::beaconNotify(osiSockAddr* /*from*/, int8 remoteTransportRevision,
|
||||
TimeStamp* timestamp, TimeStamp* startupTime, int16 sequentalID,
|
||||
PVFieldPtr /*data*/)
|
||||
{
|
||||
bool networkChanged = updateBeacon(remoteTransportRevision, timestamp, startupTime, sequentalID);
|
||||
if (networkChanged)
|
||||
changedTransport();
|
||||
}
|
||||
|
||||
bool BeaconHandler::updateBeacon(int8 /*remoteTransportRevision*/, TimeStamp* /*timestamp*/,
|
||||
TimeStamp* startupTime, int16 /*sequentalID*/)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
// first beacon notification check
|
||||
if (_serverStartupTime.getSecondsPastEpoch() == 0)
|
||||
{
|
||||
_serverStartupTime = *startupTime;
|
||||
|
||||
// new server up..
|
||||
_context.lock()->newServerDetected();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networkChange = !(_serverStartupTime == *startupTime);
|
||||
if (networkChange)
|
||||
{
|
||||
// update startup time
|
||||
_serverStartupTime = *startupTime;
|
||||
|
||||
_context.lock()->newServerDetected();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void BeaconHandler::changedTransport()
|
||||
{
|
||||
// TODO why only TCP, actually TCP does not need this
|
||||
auto_ptr<TransportRegistry::transportVector_t> transports =
|
||||
_context.lock()->getTransportRegistry()->get("TCP", &_responseFrom);
|
||||
if (!transports.get())
|
||||
return;
|
||||
|
||||
// notify all
|
||||
for (TransportRegistry::transportVector_t::iterator iter = transports->begin();
|
||||
iter != transports->end();
|
||||
iter++)
|
||||
{
|
||||
(*iter)->changedTransport();
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
102
src/remote/beaconHandler.h
Normal file
102
src/remote/beaconHandler.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BEACONHANDLER_H
|
||||
#define BEACONHANDLER_H
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define beaconHandlerEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <osiSock.h>
|
||||
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/lock.h>
|
||||
|
||||
#ifdef beaconHandlerEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef beaconHandlerEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/remote.h>
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
/**
|
||||
* BeaconHandler
|
||||
*/
|
||||
class BeaconHandler
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(BeaconHandler);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param transport transport to be used to send beacons.
|
||||
* @param context PVA context.
|
||||
*/
|
||||
BeaconHandler(Context::shared_pointer const & context, const osiSockAddr* responseFrom);
|
||||
/**
|
||||
* Test Constructor (for testing)
|
||||
* @param transport transport to be used to send beacons.
|
||||
*/
|
||||
BeaconHandler(const osiSockAddr* responseFrom);
|
||||
virtual ~BeaconHandler();
|
||||
/**
|
||||
* Update beacon period and do analitical checks (server restared, routing problems, etc.)
|
||||
* @param from who is notifying.
|
||||
* @param remoteTransportRevision encoded (major, minor) revision.
|
||||
* @param timestamp time when beacon was received.
|
||||
* @param startupTime server (reported) startup time.
|
||||
* @param sequentalID sequential ID.
|
||||
* @param data server status data, can be <code>NULL</code>.
|
||||
*/
|
||||
void beaconNotify(osiSockAddr* from,
|
||||
epics::pvData::int8 remoteTransportRevision,
|
||||
epics::pvData::TimeStamp* timestamp,
|
||||
epics::pvData::TimeStamp* startupTime,
|
||||
epics::pvData::int16 sequentalID,
|
||||
epics::pvData::PVFieldPtr data);
|
||||
private:
|
||||
/**
|
||||
* Context instance.
|
||||
*/
|
||||
Context::weak_pointer _context;
|
||||
/**
|
||||
* Remote address.
|
||||
*/
|
||||
const osiSockAddr _responseFrom;
|
||||
/**
|
||||
* Mutex
|
||||
*/
|
||||
epics::pvData::Mutex _mutex;
|
||||
/**
|
||||
* Server startup timestamp.
|
||||
*/
|
||||
epics::pvData::TimeStamp _serverStartupTime;
|
||||
/**
|
||||
* Update beacon.
|
||||
* @param remoteTransportRevision encoded (major, minor) revision.
|
||||
* @param timestamp time when beacon was received.
|
||||
* @param sequentalID sequential ID.
|
||||
* @return network change (server restarted) detected.
|
||||
*/
|
||||
bool updateBeacon(epics::pvData::int8 remoteTransportRevision,
|
||||
epics::pvData::TimeStamp* timestamp,
|
||||
epics::pvData::TimeStamp* startupTime,
|
||||
epics::pvData::int16 sequentalID);
|
||||
/**
|
||||
* Changed transport (server restarted) notify.
|
||||
*/
|
||||
void changedTransport();
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* INTROSPECTIONREGISTRY_H */
|
||||
199
src/remote/blockingTCP.h
Normal file
199
src/remote/blockingTCP.h
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BLOCKINGTCP_H_
|
||||
#define BLOCKINGTCP_H_
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define blockingTCPEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <osdSock.h>
|
||||
#include <osiSock.h>
|
||||
#include <epicsTime.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/timer.h>
|
||||
#include <pv/event.h>
|
||||
|
||||
#ifdef blockingTCPEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef blockingTCPEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvaConstants.h>
|
||||
#include <pv/remote.h>
|
||||
#include <pv/transportRegistry.h>
|
||||
#include <pv/introspectionRegistry.h>
|
||||
#include <pv/namedLockPattern.h>
|
||||
#include <pv/inetAddressUtil.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
/**
|
||||
* Channel Access TCP connector.
|
||||
* @author <a href="mailto:matej.sekoranjaATcosylab.com">Matej Sekoranja</a>
|
||||
* @version $Id: BlockingTCPConnector.java,v 1.1 2010/05/03 14:45:47 mrkraimer Exp $
|
||||
*/
|
||||
class BlockingTCPConnector : public Connector {
|
||||
public:
|
||||
POINTER_DEFINITIONS(BlockingTCPConnector);
|
||||
|
||||
BlockingTCPConnector(Context::shared_pointer const & context, int receiveBufferSize,
|
||||
float beaconInterval);
|
||||
|
||||
virtual ~BlockingTCPConnector();
|
||||
|
||||
virtual Transport::shared_pointer connect(TransportClient::shared_pointer const & client,
|
||||
std::auto_ptr<ResponseHandler>& responseHandler, osiSockAddr& address,
|
||||
epics::pvData::int8 transportRevision, epics::pvData::int16 priority);
|
||||
private:
|
||||
/**
|
||||
* Lock timeout
|
||||
*/
|
||||
static const int LOCK_TIMEOUT = 20*1000; // 20s
|
||||
|
||||
/**
|
||||
* Context instance.
|
||||
*/
|
||||
Context::weak_pointer _context;
|
||||
|
||||
/**
|
||||
* named lock
|
||||
*/
|
||||
NamedLockPattern<const osiSockAddr*, comp_osiSockAddrPtr> _namedLocker;
|
||||
|
||||
/**
|
||||
* Receive buffer size.
|
||||
*/
|
||||
int _receiveBufferSize;
|
||||
|
||||
/**
|
||||
* Beacon interval.
|
||||
*/
|
||||
float _beaconInterval;
|
||||
|
||||
/**
|
||||
* Tries to connect to the given address.
|
||||
* @param[in] address
|
||||
* @param[in] tries
|
||||
* @return the SOCKET
|
||||
* @throws IOException
|
||||
*/
|
||||
SOCKET tryConnect(osiSockAddr& address, int tries);
|
||||
|
||||
};
|
||||
|
||||
class ResponseHandlerFactory
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ResponseHandlerFactory);
|
||||
|
||||
virtual ~ResponseHandlerFactory() {};
|
||||
|
||||
virtual std::auto_ptr<ResponseHandler> createResponseHandler() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Channel Access Server TCP acceptor.
|
||||
* @author <a href="mailto:matej.sekoranjaATcosylab.com">Matej Sekoranja</a>
|
||||
* @version $Id: BlockingTCPAcceptor.java,v 1.1 2010/05/03 14:45:42 mrkraimer Exp $
|
||||
*/
|
||||
class BlockingTCPAcceptor {
|
||||
public:
|
||||
POINTER_DEFINITIONS(BlockingTCPAcceptor);
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* @param port
|
||||
* @param receiveBufferSize
|
||||
* @throws PVAException
|
||||
*/
|
||||
BlockingTCPAcceptor(Context::shared_pointer const & context,
|
||||
ResponseHandlerFactory::shared_pointer const & responseHandlerFactory,
|
||||
int port, int receiveBufferSize);
|
||||
|
||||
virtual ~BlockingTCPAcceptor();
|
||||
|
||||
void handleEvents();
|
||||
|
||||
/**
|
||||
* Bind socket address.
|
||||
* @return bind socket address, <code>null</code> if not binded.
|
||||
*/
|
||||
const osiSockAddr* getBindAddress() {
|
||||
return &_bindAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy acceptor (stop listening).
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Context instance.
|
||||
*/
|
||||
Context::shared_pointer _context;
|
||||
|
||||
/**
|
||||
* ResponseHandler factory.
|
||||
*/
|
||||
ResponseHandlerFactory::shared_pointer _responseHandlerFactory;
|
||||
|
||||
/**
|
||||
* Bind server socket address.
|
||||
*/
|
||||
osiSockAddr _bindAddress;
|
||||
|
||||
/**
|
||||
* Server socket channel.
|
||||
*/
|
||||
SOCKET _serverSocketChannel;
|
||||
|
||||
/**
|
||||
* Receive buffer size.
|
||||
*/
|
||||
int _receiveBufferSize;
|
||||
|
||||
/**
|
||||
* Destroyed flag.
|
||||
*/
|
||||
bool _destroyed;
|
||||
|
||||
epics::pvData::Mutex _mutex;
|
||||
|
||||
epicsThreadId _threadId;
|
||||
|
||||
/**
|
||||
* Initialize connection acception.
|
||||
* @return port where server is listening
|
||||
*/
|
||||
int initialize(unsigned short port);
|
||||
|
||||
/**
|
||||
* Validate connection by sending a validation message request.
|
||||
* @return <code>true</code> on success.
|
||||
*/
|
||||
bool validateConnection(Transport::shared_pointer const & transport, const char* address);
|
||||
|
||||
static void handleEventsRunner(void* param);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* BLOCKINGTCP_H_ */
|
||||
253
src/remote/blockingTCPAcceptor.cpp
Normal file
253
src/remote/blockingTCPAcceptor.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
/**
|
||||
* 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/blockingTCP.h>
|
||||
#include "codec.h"
|
||||
#include <pv/remote.h>
|
||||
#include <pv/logger.h>
|
||||
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
#include <osiSock.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using std::ostringstream;
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
BlockingTCPAcceptor::BlockingTCPAcceptor(
|
||||
Context::shared_pointer const & context,
|
||||
ResponseHandlerFactory::shared_pointer const & responseHandlerFactory,
|
||||
int port,
|
||||
int receiveBufferSize) :
|
||||
_context(context),
|
||||
_responseHandlerFactory(responseHandlerFactory),
|
||||
_bindAddress(),
|
||||
_serverSocketChannel(INVALID_SOCKET),
|
||||
_receiveBufferSize(receiveBufferSize),
|
||||
_destroyed(false),
|
||||
_threadId(0)
|
||||
{
|
||||
initialize(port);
|
||||
}
|
||||
|
||||
BlockingTCPAcceptor::~BlockingTCPAcceptor() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
int BlockingTCPAcceptor::initialize(unsigned short port) {
|
||||
// specified bind address
|
||||
_bindAddress.ia.sin_family = AF_INET;
|
||||
_bindAddress.ia.sin_port = htons(port);
|
||||
_bindAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
char strBuffer[64];
|
||||
char ipAddrStr[48];
|
||||
ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr));
|
||||
|
||||
int tryCount = 0;
|
||||
while(tryCount<2) {
|
||||
|
||||
LOG(logLevelDebug, "Creating acceptor to %s.", ipAddrStr);
|
||||
|
||||
_serverSocketChannel = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if(_serverSocketChannel==INVALID_SOCKET) {
|
||||
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
|
||||
ostringstream temp;
|
||||
temp<<"Socket create error: "<<strBuffer;
|
||||
LOG(logLevelError, "%s", temp.str().c_str());
|
||||
THROW_BASE_EXCEPTION(temp.str().c_str());
|
||||
}
|
||||
else {
|
||||
|
||||
//epicsSocketEnableAddressReuseDuringTimeWaitState(_serverSocketChannel);
|
||||
|
||||
// try to bind
|
||||
int retval = ::bind(_serverSocketChannel, &_bindAddress.sa, sizeof(sockaddr));
|
||||
if(retval<0) {
|
||||
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
|
||||
LOG(logLevelDebug, "Socket bind error: %s", strBuffer);
|
||||
if(_bindAddress.ia.sin_port!=0) {
|
||||
// failed to bind to specified bind address,
|
||||
// try to get port dynamically, but only once
|
||||
LOG(
|
||||
logLevelDebug,
|
||||
"Configured TCP port %d is unavailable, trying to assign it dynamically.",
|
||||
port);
|
||||
_bindAddress.ia.sin_port = htons(0);
|
||||
}
|
||||
else {
|
||||
epicsSocketDestroy(_serverSocketChannel);
|
||||
break; // exit while loop
|
||||
}
|
||||
}
|
||||
else { // if(retval<0)
|
||||
// bind succeeded
|
||||
|
||||
// update bind address, if dynamically port selection was used
|
||||
if(ntohs(_bindAddress.ia.sin_port)==0) {
|
||||
osiSocklen_t sockLen = sizeof(sockaddr);
|
||||
// read the actual socket info
|
||||
retval = ::getsockname(_serverSocketChannel, &_bindAddress.sa, &sockLen);
|
||||
if(retval<0) {
|
||||
// error obtaining port number
|
||||
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
|
||||
LOG(logLevelDebug, "getsockname error: %s", strBuffer);
|
||||
}
|
||||
else {
|
||||
LOG(
|
||||
logLevelInfo,
|
||||
"Using dynamically assigned TCP port %d.",
|
||||
ntohs(_bindAddress.ia.sin_port));
|
||||
}
|
||||
}
|
||||
|
||||
retval = ::listen(_serverSocketChannel, 1024);
|
||||
if(retval<0) {
|
||||
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
|
||||
ostringstream temp;
|
||||
temp<<"Socket listen error: "<<strBuffer;
|
||||
LOG(logLevelError, "%s", temp.str().c_str());
|
||||
THROW_BASE_EXCEPTION(temp.str().c_str());
|
||||
}
|
||||
|
||||
_threadId
|
||||
= epicsThreadCreate(
|
||||
"TCP-acceptor",
|
||||
epicsThreadPriorityMedium,
|
||||
epicsThreadGetStackSize(
|
||||
epicsThreadStackMedium),
|
||||
BlockingTCPAcceptor::handleEventsRunner,
|
||||
this);
|
||||
|
||||
// all OK, return
|
||||
return ntohs(_bindAddress.ia.sin_port);
|
||||
} // successful bind
|
||||
} // successfully obtained socket
|
||||
tryCount++;
|
||||
} // while
|
||||
|
||||
ostringstream temp;
|
||||
temp<<"Failed to create acceptor to "<<ipAddrStr;
|
||||
THROW_BASE_EXCEPTION(temp.str().c_str());
|
||||
}
|
||||
|
||||
void BlockingTCPAcceptor::handleEvents() {
|
||||
// rise level if port is assigned dynamically
|
||||
char ipAddrStr[48];
|
||||
ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr));
|
||||
LOG(logLevelDebug, "Accepting connections at %s.", ipAddrStr);
|
||||
|
||||
bool socketOpen = true;
|
||||
char strBuffer[64];
|
||||
|
||||
while(socketOpen) {
|
||||
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
if (_destroyed)
|
||||
break;
|
||||
}
|
||||
|
||||
osiSockAddr address;
|
||||
osiSocklen_t len = sizeof(sockaddr);
|
||||
|
||||
SOCKET newClient = epicsSocketAccept(_serverSocketChannel, &address.sa, &len);
|
||||
if(newClient!=INVALID_SOCKET) {
|
||||
// accept succeeded
|
||||
ipAddrToDottedIP(&address.ia, ipAddrStr, sizeof(ipAddrStr));
|
||||
LOG(logLevelDebug, "Accepted connection from PVA client: %s", ipAddrStr);
|
||||
|
||||
// enable TCP_NODELAY (disable Nagle's algorithm)
|
||||
int optval = 1; // true
|
||||
int retval = ::setsockopt(newClient, IPPROTO_TCP, TCP_NODELAY, (char *)&optval, sizeof(int));
|
||||
if(retval<0) {
|
||||
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
|
||||
LOG(logLevelDebug, "Error setting TCP_NODELAY: %s", strBuffer);
|
||||
}
|
||||
|
||||
// enable TCP_KEEPALIVE
|
||||
retval = ::setsockopt(newClient, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(int));
|
||||
if(retval<0) {
|
||||
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
|
||||
LOG(logLevelDebug, "Error setting SO_KEEPALIVE: %s", strBuffer);
|
||||
}
|
||||
|
||||
// TODO tune buffer sizes?!
|
||||
|
||||
// get TCP send buffer size
|
||||
osiSocklen_t intLen = sizeof(int);
|
||||
int _socketSendBufferSize;
|
||||
retval = getsockopt(newClient, SOL_SOCKET, SO_SNDBUF, (char *)&_socketSendBufferSize, &intLen);
|
||||
if(retval<0) {
|
||||
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
|
||||
LOG(logLevelDebug, "Error getting SO_SNDBUF: %s", strBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create transport, it registers itself to the registry.
|
||||
*/
|
||||
std::auto_ptr<ResponseHandler> responseHandler = _responseHandlerFactory->createResponseHandler();
|
||||
detail::BlockingServerTCPTransportCodec::shared_pointer transport =
|
||||
detail::BlockingServerTCPTransportCodec::create(
|
||||
_context,
|
||||
newClient,
|
||||
responseHandler,
|
||||
_socketSendBufferSize,
|
||||
_receiveBufferSize);
|
||||
|
||||
// validate connection
|
||||
if(!validateConnection(transport, ipAddrStr)) {
|
||||
transport->close();
|
||||
LOG(
|
||||
logLevelDebug,
|
||||
"Connection to PVA client %s failed to be validated, closing it.",
|
||||
ipAddrStr);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(logLevelDebug, "Serving to PVA client: %s", ipAddrStr);
|
||||
|
||||
}// accept succeeded
|
||||
else
|
||||
socketOpen = false;
|
||||
} // while
|
||||
}
|
||||
|
||||
bool BlockingTCPAcceptor::validateConnection(Transport::shared_pointer const & transport, const char* address) {
|
||||
try {
|
||||
transport->verify(0);
|
||||
return true;
|
||||
} catch(...) {
|
||||
LOG(logLevelDebug, "Validation of %s failed.", address);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void BlockingTCPAcceptor::handleEventsRunner(void* param) {
|
||||
((BlockingTCPAcceptor*)param)->handleEvents();
|
||||
}
|
||||
|
||||
void BlockingTCPAcceptor::destroy() {
|
||||
Lock guard(_mutex);
|
||||
if(_destroyed) return;
|
||||
_destroyed = true;
|
||||
|
||||
if(_serverSocketChannel!=INVALID_SOCKET) {
|
||||
char ipAddrStr[48];
|
||||
ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr));
|
||||
LOG(logLevelDebug, "Stopped accepting connections at %s.", ipAddrStr);
|
||||
|
||||
epicsSocketDestroy(_serverSocketChannel);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
198
src/remote/blockingTCPConnector.cpp
Normal file
198
src/remote/blockingTCPConnector.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* 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/blockingTCP.h>
|
||||
#include <pv/remote.h>
|
||||
#include <pv/namedLockPattern.h>
|
||||
#include <pv/logger.h>
|
||||
#include <pv/codec.h>
|
||||
|
||||
#include <epicsThread.h>
|
||||
#include <osiSock.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sstream>
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
BlockingTCPConnector::BlockingTCPConnector(
|
||||
Context::shared_pointer const & context,
|
||||
int receiveBufferSize,
|
||||
float beaconInterval) :
|
||||
_context(context),
|
||||
_namedLocker(),
|
||||
_receiveBufferSize(receiveBufferSize),
|
||||
_beaconInterval(beaconInterval)
|
||||
{
|
||||
}
|
||||
|
||||
BlockingTCPConnector::~BlockingTCPConnector() {
|
||||
}
|
||||
|
||||
SOCKET BlockingTCPConnector::tryConnect(osiSockAddr& address, int tries) {
|
||||
|
||||
char strBuffer[64];
|
||||
ipAddrToDottedIP(&address.ia, strBuffer, sizeof(strBuffer));
|
||||
|
||||
for(int tryCount = 0; tryCount<tries; tryCount++) {
|
||||
|
||||
LOG(logLevelDebug,
|
||||
"Opening socket to PVA server %s, attempt %d.",
|
||||
strBuffer, tryCount+1);
|
||||
|
||||
SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (socket == INVALID_SOCKET)
|
||||
{
|
||||
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
|
||||
LOG(logLevelWarn, "Socket create error: %s", strBuffer);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
else {
|
||||
if(::connect(socket, &address.sa, sizeof(sockaddr))==0) {
|
||||
return socket;
|
||||
}
|
||||
else {
|
||||
epicsSocketDestroy (socket);
|
||||
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
|
||||
LOG(logLevelDebug, "Socket connect error: %s", strBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
Transport::shared_pointer BlockingTCPConnector::connect(TransportClient::shared_pointer const & client,
|
||||
std::auto_ptr<ResponseHandler>& responseHandler, osiSockAddr& address,
|
||||
int8 transportRevision, int16 priority) {
|
||||
|
||||
SOCKET socket = INVALID_SOCKET;
|
||||
|
||||
char ipAddrStr[64];
|
||||
ipAddrToDottedIP(&address.ia, ipAddrStr, sizeof(ipAddrStr));
|
||||
|
||||
Context::shared_pointer context = _context.lock();
|
||||
|
||||
// first try to check cache w/o named lock...
|
||||
Transport::shared_pointer transport = context->getTransportRegistry()->get("TCP", &address, priority);
|
||||
if(transport.get()) {
|
||||
LOG(logLevelDebug,
|
||||
"Reusing existing connection to PVA server: %s",
|
||||
ipAddrStr);
|
||||
if (transport->acquire(client))
|
||||
return transport;
|
||||
}
|
||||
|
||||
bool lockAcquired = _namedLocker.acquireSynchronizationObject(&address, LOCK_TIMEOUT);
|
||||
if(lockAcquired) {
|
||||
try {
|
||||
// ... transport created during waiting in lock
|
||||
transport = context->getTransportRegistry()->get("TCP", &address, priority);
|
||||
if(transport.get()) {
|
||||
LOG(logLevelDebug,
|
||||
"Reusing existing connection to PVA server: %s",
|
||||
ipAddrStr);
|
||||
if (transport->acquire(client))
|
||||
return transport;
|
||||
}
|
||||
|
||||
LOG(logLevelDebug, "Connecting to PVA server: %s", ipAddrStr);
|
||||
|
||||
socket = tryConnect(address, 3);
|
||||
|
||||
// verify
|
||||
if(socket==INVALID_SOCKET) {
|
||||
LOG(logLevelDebug,
|
||||
"Connection to PVA server %s failed.", ipAddrStr);
|
||||
std::ostringstream temp;
|
||||
temp<<"Failed to verify TCP connection to '"<<ipAddrStr<<"'.";
|
||||
THROW_BASE_EXCEPTION(temp.str().c_str());
|
||||
}
|
||||
|
||||
LOG(logLevelDebug, "Socket connected to PVA server: %s.", ipAddrStr);
|
||||
|
||||
// enable TCP_NODELAY (disable Nagle's algorithm)
|
||||
int optval = 1; // true
|
||||
int retval = ::setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
|
||||
(char *)&optval, sizeof(int));
|
||||
if(retval<0) {
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
LOG(logLevelWarn, "Error setting TCP_NODELAY: %s", errStr);
|
||||
}
|
||||
|
||||
// enable TCP_KEEPALIVE
|
||||
retval = ::setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE,
|
||||
(char *)&optval, sizeof(int));
|
||||
if(retval<0)
|
||||
{
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
LOG(logLevelWarn, "Error setting SO_KEEPALIVE: %s", errStr);
|
||||
}
|
||||
|
||||
// TODO tune buffer sizes?! Win32 defaults are 8k, which is OK
|
||||
|
||||
// create transport
|
||||
// TODO introduce factory
|
||||
// get TCP send buffer size
|
||||
osiSocklen_t intLen = sizeof(int);
|
||||
int _socketSendBufferSize;
|
||||
retval = getsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&_socketSendBufferSize, &intLen);
|
||||
if(retval<0) {
|
||||
char strBuffer[64];
|
||||
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
|
||||
LOG(logLevelDebug, "Error getting SO_SNDBUF: %s", strBuffer);
|
||||
}
|
||||
|
||||
transport = detail::BlockingClientTCPTransportCodec::create(
|
||||
context, socket, responseHandler, _receiveBufferSize, _socketSendBufferSize,
|
||||
client, transportRevision, _beaconInterval, priority);
|
||||
|
||||
// verify
|
||||
if(!transport->verify(3000)) {
|
||||
LOG(
|
||||
logLevelDebug,
|
||||
"Connection to PVA server %s failed to be validated, closing it.",
|
||||
ipAddrStr);
|
||||
|
||||
std::ostringstream temp;
|
||||
temp<<"Failed to verify TCP connection to '"<<ipAddrStr<<"'.";
|
||||
THROW_BASE_EXCEPTION(temp.str().c_str());
|
||||
}
|
||||
|
||||
// TODO send security token
|
||||
|
||||
LOG(logLevelDebug, "Connected to PVA server: %s", ipAddrStr);
|
||||
|
||||
_namedLocker.releaseSynchronizationObject(&address);
|
||||
return transport;
|
||||
} catch(std::exception&) {
|
||||
if(transport.get())
|
||||
transport->close();
|
||||
else if(socket!=INVALID_SOCKET) epicsSocketDestroy(socket);
|
||||
_namedLocker.releaseSynchronizationObject(&address);
|
||||
throw;
|
||||
} catch(...) {
|
||||
if(transport.get())
|
||||
transport->close();
|
||||
else if(socket!=INVALID_SOCKET) epicsSocketDestroy(socket);
|
||||
_namedLocker.releaseSynchronizationObject(&address);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::ostringstream temp;
|
||||
temp<<"Failed to obtain synchronization lock for '"<<ipAddrStr;
|
||||
temp<<"', possible deadlock.";
|
||||
THROW_BASE_EXCEPTION(temp.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
378
src/remote/blockingUDP.h
Normal file
378
src/remote/blockingUDP.h
Normal file
@@ -0,0 +1,378 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BLOCKINGUDP_H_
|
||||
#define BLOCKINGUDP_H_
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define blockingUDPEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <osdSock.h>
|
||||
#include <osiSock.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#include <pv/noDefaultMethods.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
|
||||
#ifdef blockingUDPEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef blockingUDPEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/remote.h>
|
||||
#include <pv/pvaConstants.h>
|
||||
#include <pv/inetAddressUtil.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class BlockingUDPTransport : public epics::pvData::NoDefaultMethods,
|
||||
public Transport,
|
||||
public TransportSendControl,
|
||||
public std::tr1::enable_shared_from_this<BlockingUDPTransport>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(BlockingUDPTransport);
|
||||
|
||||
private:
|
||||
BlockingUDPTransport(std::auto_ptr<ResponseHandler>& responseHandler,
|
||||
SOCKET channel, osiSockAddr& bindAddress,
|
||||
short remoteTransportRevision);
|
||||
public:
|
||||
static shared_pointer create(std::auto_ptr<ResponseHandler>& responseHandler,
|
||||
SOCKET channel, osiSockAddr& bindAddress,
|
||||
short remoteTransportRevision)
|
||||
{
|
||||
shared_pointer thisPointer(
|
||||
new BlockingUDPTransport(responseHandler, channel, bindAddress, remoteTransportRevision)
|
||||
);
|
||||
return thisPointer;
|
||||
}
|
||||
|
||||
virtual ~BlockingUDPTransport();
|
||||
|
||||
virtual bool isClosed() {
|
||||
return _closed.get();
|
||||
}
|
||||
|
||||
virtual const osiSockAddr* getRemoteAddress() const {
|
||||
// always connected
|
||||
return &_bindAddress;
|
||||
}
|
||||
|
||||
virtual epics::pvData::String getType() const {
|
||||
return epics::pvData::String("UDP");
|
||||
}
|
||||
|
||||
virtual std::size_t getReceiveBufferSize() const {
|
||||
return _receiveBuffer->getSize();
|
||||
}
|
||||
|
||||
virtual std::size_t getSocketReceiveBufferSize() const;
|
||||
|
||||
virtual epics::pvData::int16 getPriority() const {
|
||||
return PVA_DEFAULT_PRIORITY;
|
||||
}
|
||||
|
||||
virtual epics::pvData::int8 getRevision() const {
|
||||
return PVA_PROTOCOL_REVISION;
|
||||
}
|
||||
|
||||
virtual void setRemoteRevision(epics::pvData::int8 /*revision*/) {
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual void setRemoteTransportReceiveBufferSize(
|
||||
std::size_t /*receiveBufferSize*/) {
|
||||
// noop for UDP (limited by 64k; MAX_UDP_SEND for PVA)
|
||||
}
|
||||
|
||||
virtual void setRemoteTransportSocketReceiveBufferSize(
|
||||
std::size_t /*socketReceiveBufferSize*/) {
|
||||
// noop for UDP (limited by 64k; MAX_UDP_SEND for PVA)
|
||||
}
|
||||
|
||||
virtual void aliveNotification() {
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual void changedTransport() {
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual bool verify(epics::pvData::int32 /*timeoutMs*/) {
|
||||
// noop
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void verified() {
|
||||
// noop
|
||||
}
|
||||
|
||||
// NOTE: this is not yet used for UDP
|
||||
virtual void setByteOrder(int byteOrder) {
|
||||
// called from receive thread... or before processing
|
||||
_receiveBuffer->setEndianess(byteOrder);
|
||||
|
||||
// sync?!
|
||||
_sendBuffer->setEndianess(byteOrder);
|
||||
}
|
||||
|
||||
virtual void enqueueSendRequest(TransportSender::shared_pointer const & sender);
|
||||
|
||||
virtual void flushSendQueue();
|
||||
|
||||
void start();
|
||||
|
||||
virtual void close();
|
||||
|
||||
virtual void ensureData(std::size_t /*size*/) {
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual void alignData(std::size_t alignment) {
|
||||
_receiveBuffer->align(alignment);
|
||||
}
|
||||
|
||||
virtual bool directSerialize(epics::pvData::ByteBuffer* /*existingBuffer*/, const char* /*toSerialize*/,
|
||||
std::size_t /*elementCount*/, std::size_t /*elementSize*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool directDeserialize(epics::pvData::ByteBuffer* /*existingBuffer*/, char* /*deserializeTo*/,
|
||||
std::size_t /*elementCount*/, std::size_t /*elementSize*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void startMessage(epics::pvData::int8 command, std::size_t ensureCapacity);
|
||||
virtual void endMessage();
|
||||
|
||||
virtual void flush(bool /*lastMessageCompleted*/) {
|
||||
// noop since all UDP requests are sent immediately
|
||||
}
|
||||
|
||||
virtual void setRecipient(const osiSockAddr& sendTo) {
|
||||
_sendToEnabled = true;
|
||||
_sendTo = sendTo;
|
||||
}
|
||||
|
||||
virtual void flushSerializeBuffer() {
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual void ensureBuffer(std::size_t /*size*/) {
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual void alignBuffer(std::size_t alignment) {
|
||||
_sendBuffer->align(alignment);
|
||||
}
|
||||
|
||||
virtual void cachedSerialize(
|
||||
const std::tr1::shared_ptr<const epics::pvData::Field>& field, epics::pvData::ByteBuffer* buffer)
|
||||
{
|
||||
// no cache
|
||||
field->serialize(buffer, this);
|
||||
}
|
||||
|
||||
virtual std::tr1::shared_ptr<const epics::pvData::Field>
|
||||
cachedDeserialize(epics::pvData::ByteBuffer* buffer)
|
||||
{
|
||||
// no cache
|
||||
// TODO
|
||||
return epics::pvData::getFieldCreate()->deserialize(buffer, this);
|
||||
}
|
||||
|
||||
virtual bool acquire(std::tr1::shared_ptr<TransportClient> const & /*client*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void release(pvAccessID /*clientId*/) {}
|
||||
|
||||
/**
|
||||
* Set ignore list.
|
||||
* @param addresses list of ignored addresses.
|
||||
*/
|
||||
void setIgnoredAddresses(InetAddrVector* addresses) {
|
||||
if (addresses)
|
||||
{
|
||||
if (!_ignoredAddresses) _ignoredAddresses = new InetAddrVector;
|
||||
*_ignoredAddresses = *addresses;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_ignoredAddresses) { delete _ignoredAddresses; _ignoredAddresses = 0; }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of ignored addresses.
|
||||
* @return ignored addresses.
|
||||
*/
|
||||
InetAddrVector* getIgnoredAddresses() const {
|
||||
return _ignoredAddresses;
|
||||
}
|
||||
|
||||
bool send(epics::pvData::ByteBuffer* buffer, const osiSockAddr& address);
|
||||
|
||||
bool send(epics::pvData::ByteBuffer* buffer);
|
||||
|
||||
/**
|
||||
* Get list of send addresses.
|
||||
* @return send addresses.
|
||||
*/
|
||||
InetAddrVector* getSendAddresses() {
|
||||
return _sendAddresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bind address.
|
||||
* @return bind address.
|
||||
*/
|
||||
const osiSockAddr* getBindAddress() const {
|
||||
return &_bindAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set list of send addresses.
|
||||
* @param addresses list of send addresses, non-<code>null</code>.
|
||||
*/
|
||||
void setBroadcastAddresses(InetAddrVector* addresses) {
|
||||
if (addresses)
|
||||
{
|
||||
if (!_sendAddresses) _sendAddresses = new InetAddrVector;
|
||||
*_sendAddresses = *addresses;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_sendAddresses) { delete _sendAddresses; _sendAddresses = 0; }
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
AtomicBoolean _closed;
|
||||
|
||||
/**
|
||||
* Response handler.
|
||||
*/
|
||||
std::auto_ptr<ResponseHandler> _responseHandler;
|
||||
|
||||
virtual void processRead();
|
||||
|
||||
private:
|
||||
static void threadRunner(void* param);
|
||||
|
||||
bool processBuffer(Transport::shared_pointer const & transport, osiSockAddr& fromAddress, epics::pvData::ByteBuffer* receiveBuffer);
|
||||
|
||||
void close(bool waitForThreadToComplete);
|
||||
|
||||
// Context only used for logging in this class
|
||||
|
||||
/**
|
||||
* Corresponding channel.
|
||||
*/
|
||||
SOCKET _channel;
|
||||
|
||||
/**
|
||||
* Bind address.
|
||||
*/
|
||||
osiSockAddr _bindAddress;
|
||||
|
||||
/**
|
||||
* Send addresses.
|
||||
*/
|
||||
InetAddrVector* _sendAddresses;
|
||||
|
||||
/**
|
||||
* Ignore addresses.
|
||||
*/
|
||||
InetAddrVector* _ignoredAddresses;
|
||||
|
||||
/**
|
||||
* Send address.
|
||||
*/
|
||||
osiSockAddr _sendTo;
|
||||
bool _sendToEnabled;
|
||||
|
||||
/**
|
||||
* Receive buffer.
|
||||
*/
|
||||
std::auto_ptr<epics::pvData::ByteBuffer> _receiveBuffer;
|
||||
|
||||
/**
|
||||
* Send buffer.
|
||||
*/
|
||||
std::auto_ptr<epics::pvData::ByteBuffer> _sendBuffer;
|
||||
|
||||
/**
|
||||
* Last message start position.
|
||||
*/
|
||||
int _lastMessageStartPosition;
|
||||
|
||||
/**
|
||||
* Used for process sync.
|
||||
*/
|
||||
epics::pvData::Mutex _mutex;
|
||||
epics::pvData::Mutex _sendMutex;
|
||||
epics::pvData::Event _shutdownEvent;
|
||||
|
||||
/**
|
||||
* Thread ID
|
||||
*/
|
||||
epicsThreadId _threadId;
|
||||
|
||||
};
|
||||
|
||||
class BlockingUDPConnector :
|
||||
public Connector,
|
||||
private epics::pvData::NoDefaultMethods {
|
||||
public:
|
||||
POINTER_DEFINITIONS(BlockingUDPConnector);
|
||||
|
||||
BlockingUDPConnector(
|
||||
bool reuseSocket,
|
||||
bool broadcast) :
|
||||
_reuseSocket(reuseSocket),
|
||||
_broadcast(broadcast) {
|
||||
}
|
||||
|
||||
virtual ~BlockingUDPConnector() {
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: transport client is ignored for broadcast (UDP).
|
||||
*/
|
||||
virtual Transport::shared_pointer connect(TransportClient::shared_pointer const & client,
|
||||
std::auto_ptr<ResponseHandler>& responseHandler, osiSockAddr& bindAddress,
|
||||
epics::pvData::int8 transportRevision, epics::pvData::int16 priority);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Reuse socket flag.
|
||||
*/
|
||||
bool _reuseSocket;
|
||||
|
||||
/**
|
||||
* Broadcast flag.
|
||||
*/
|
||||
bool _broadcast;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* BLOCKINGUDP_H_ */
|
||||
70
src/remote/blockingUDPConnector.cpp
Normal file
70
src/remote/blockingUDPConnector.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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/blockingUDP.h>
|
||||
#include <pv/remote.h>
|
||||
#include <pv/logger.h>
|
||||
|
||||
#include <osiSock.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
Transport::shared_pointer BlockingUDPConnector::connect(TransportClient::shared_pointer const & /*client*/,
|
||||
auto_ptr<ResponseHandler>& responseHandler, osiSockAddr& bindAddress,
|
||||
int8 transportRevision, int16 /*priority*/) {
|
||||
|
||||
LOG(logLevelDebug, "Creating datagram socket to: %s",
|
||||
inetAddressToString(bindAddress).c_str());
|
||||
|
||||
SOCKET socket = epicsSocketCreate(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if(socket==INVALID_SOCKET) {
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
LOG(logLevelError, "Error creating socket: %s", errStr);
|
||||
return Transport::shared_pointer();
|
||||
}
|
||||
|
||||
int optval = _broadcast ? 1 : 0;
|
||||
int retval = ::setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof(optval));
|
||||
if(retval<0)
|
||||
{
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
LOG(logLevelError, "Error setting SO_BROADCAST: %s", errStr);
|
||||
epicsSocketDestroy (socket);
|
||||
return Transport::shared_pointer();
|
||||
}
|
||||
|
||||
/*
|
||||
IPv4 multicast addresses are defined by the leading address bits of 1110, originating from the classful network design of the early Internet when this group of addresses was designated as Class D. The Classless Inter-Domain Routing (CIDR) prefix of this group is 224.0.0.0/4. The group includes the addresses from 224.0.0.0 to 239.255.255.255. Address assignments from within this range are specified in RFC 5771, an Internet Engineering Task Force (IETF) Best Current Practice document (BCP 51).*/
|
||||
|
||||
|
||||
// set SO_REUSEADDR or SO_REUSEPORT, OS dependant
|
||||
if (_reuseSocket)
|
||||
epicsSocketEnableAddressUseForDatagramFanout(socket);
|
||||
|
||||
retval = ::bind(socket, (sockaddr*)&(bindAddress.sa), sizeof(sockaddr));
|
||||
if(retval<0) {
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
LOG(logLevelError, "Error binding socket: %s", errStr);
|
||||
epicsSocketDestroy (socket);
|
||||
return Transport::shared_pointer();
|
||||
}
|
||||
|
||||
// sockets are blocking by default
|
||||
Transport::shared_pointer transport = BlockingUDPTransport::create(responseHandler, socket, bindAddress, transportRevision);
|
||||
return transport;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
393
src/remote/blockingUDPTransport.cpp
Normal file
393
src/remote/blockingUDPTransport.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
/**
|
||||
* 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/blockingUDP.h>
|
||||
#include <pv/pvaConstants.h>
|
||||
#include <pv/inetAddressUtil.h>
|
||||
#include <pv/logger.h>
|
||||
#include <pv/likely.h>
|
||||
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/lock.h>
|
||||
|
||||
#include <osdSock.h>
|
||||
#include <osiSock.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <sys/types.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
#ifdef __vxworks
|
||||
inline int sendto(int s, const char *buf, size_t len, int flags, const struct sockaddr *to, int tolen)
|
||||
{
|
||||
return ::sendto(s, const_cast<char*>(buf), len, flags, const_cast<struct sockaddr *>(to), tolen);
|
||||
}
|
||||
#endif
|
||||
|
||||
PVACCESS_REFCOUNT_MONITOR_DEFINE(blockingUDPTransport);
|
||||
|
||||
BlockingUDPTransport::BlockingUDPTransport(
|
||||
auto_ptr<ResponseHandler>& responseHandler, SOCKET channel,
|
||||
osiSockAddr& bindAddress,
|
||||
short /*remoteTransportRevision*/) :
|
||||
_closed(),
|
||||
_responseHandler(responseHandler),
|
||||
_channel(channel),
|
||||
_bindAddress(bindAddress),
|
||||
_sendAddresses(0),
|
||||
_ignoredAddresses(0),
|
||||
_sendToEnabled(false),
|
||||
_receiveBuffer(new ByteBuffer(MAX_UDP_RECV)),
|
||||
_sendBuffer(new ByteBuffer(MAX_UDP_RECV)),
|
||||
_lastMessageStartPosition(0),
|
||||
_threadId(0)
|
||||
{
|
||||
PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(blockingUDPTransport);
|
||||
|
||||
// set receive timeout so that we do not have problems at shutdown (recvfrom would block)
|
||||
struct timeval timeout;
|
||||
memset(&timeout, 0, sizeof(struct timeval));
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
if (unlikely(::setsockopt (_channel, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0))
|
||||
{
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
LOG(logLevelError,
|
||||
"Failed to set SO_RCVTIMEO for UDP socket %s: %s.",
|
||||
inetAddressToString(_bindAddress).c_str(), errStr);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
BlockingUDPTransport::~BlockingUDPTransport() {
|
||||
PVACCESS_REFCOUNT_MONITOR_DESTRUCT(blockingUDPTransport);
|
||||
|
||||
close(true); // close the socket and stop the thread.
|
||||
|
||||
// TODO use auto_ptr class members
|
||||
|
||||
if (_sendAddresses) delete _sendAddresses;
|
||||
if (_ignoredAddresses) delete _ignoredAddresses;
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::start() {
|
||||
|
||||
String threadName = "UDP-receive "+inetAddressToString(_bindAddress);
|
||||
LOG(logLevelDebug, "Starting thread: %s",threadName.c_str());
|
||||
|
||||
_threadId = epicsThreadCreate(threadName.c_str(),
|
||||
epicsThreadPriorityMedium,
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
BlockingUDPTransport::threadRunner, this);
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::close() {
|
||||
close(true);
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::close(bool waitForThreadToComplete) {
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
if(_closed.get()) return;
|
||||
_closed.set();
|
||||
|
||||
LOG(logLevelDebug,
|
||||
"UDP socket %s closed.",
|
||||
inetAddressToString(_bindAddress).c_str());
|
||||
|
||||
epicsSocketSystemCallInterruptMechanismQueryInfo info =
|
||||
epicsSocketSystemCallInterruptMechanismQuery ();
|
||||
switch ( info ) {
|
||||
case esscimqi_socketCloseRequired:
|
||||
epicsSocketDestroy ( _channel );
|
||||
break;
|
||||
case esscimqi_socketBothShutdownRequired:
|
||||
{
|
||||
int status = ::shutdown ( _channel, SHUT_RDWR );
|
||||
if ( status ) {
|
||||
char sockErrBuf[64];
|
||||
epicsSocketConvertErrnoToString (
|
||||
sockErrBuf, sizeof ( sockErrBuf ) );
|
||||
LOG(logLevelDebug,
|
||||
"UDP socket %s failed to shutdown: %s.",
|
||||
inetAddressToString(_bindAddress).c_str(), sockErrBuf);
|
||||
}
|
||||
epicsSocketDestroy ( _channel );
|
||||
}
|
||||
break;
|
||||
case esscimqi_socketSigAlarmRequired:
|
||||
// TODO (not supported anymore anyway)
|
||||
default:
|
||||
epicsSocketDestroy(_channel);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO send yourself a packet
|
||||
|
||||
// wait for send thread to exit cleanly
|
||||
if (waitForThreadToComplete)
|
||||
{
|
||||
if (!_shutdownEvent.wait(5.0))
|
||||
{
|
||||
LOG(logLevelError,
|
||||
"Receive thread for UDP socket %s has not exited.",
|
||||
inetAddressToString(_bindAddress).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::enqueueSendRequest(TransportSender::shared_pointer const & sender) {
|
||||
Lock lock(_sendMutex);
|
||||
|
||||
_sendToEnabled = false;
|
||||
_sendBuffer->clear();
|
||||
sender->lock();
|
||||
try {
|
||||
sender->send(_sendBuffer.get(), this);
|
||||
sender->unlock();
|
||||
endMessage();
|
||||
if(!_sendToEnabled)
|
||||
send(_sendBuffer.get());
|
||||
else
|
||||
send(_sendBuffer.get(), _sendTo);
|
||||
} catch(...) {
|
||||
sender->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BlockingUDPTransport::flushSendQueue()
|
||||
{
|
||||
// noop (note different sent addresses are possible)
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::startMessage(int8 command, size_t /*ensureCapacity*/) {
|
||||
_lastMessageStartPosition = _sendBuffer->getPosition();
|
||||
_sendBuffer->putByte(PVA_MAGIC);
|
||||
_sendBuffer->putByte(PVA_VERSION);
|
||||
_sendBuffer->putByte((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) ? 0x80 : 0x00); // data + 7-bit endianess
|
||||
_sendBuffer->putByte(command); // command
|
||||
_sendBuffer->putInt(0); // temporary zero payload
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::endMessage() {
|
||||
//we always (for now) send by packet, so no need for this here...
|
||||
//alignBuffer(PVA_ALIGNMENT);
|
||||
_sendBuffer->putInt(
|
||||
_lastMessageStartPosition+(sizeof(int16)+2),
|
||||
_sendBuffer->getPosition()-_lastMessageStartPosition-PVA_MESSAGE_HEADER_SIZE);
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::processRead() {
|
||||
// This function is always called from only one thread - this
|
||||
// object's own thread.
|
||||
|
||||
osiSockAddr fromAddress;
|
||||
osiSocklen_t addrStructSize = sizeof(sockaddr);
|
||||
Transport::shared_pointer thisTransport = shared_from_this();
|
||||
|
||||
try {
|
||||
|
||||
while(!_closed.get())
|
||||
{
|
||||
// we poll to prevent blocking indefinitely
|
||||
|
||||
// data ready to be read
|
||||
_receiveBuffer->clear();
|
||||
|
||||
int bytesRead = recvfrom(_channel, (char*)_receiveBuffer->getArray(),
|
||||
_receiveBuffer->getRemaining(), 0, (sockaddr*)&fromAddress,
|
||||
&addrStructSize);
|
||||
|
||||
if(likely(bytesRead>0)) {
|
||||
// successfully got datagram
|
||||
bool ignore = false;
|
||||
if(likely(_ignoredAddresses!=0))
|
||||
{
|
||||
for(size_t i = 0; i <_ignoredAddresses->size(); i++)
|
||||
{
|
||||
if((*_ignoredAddresses)[i].ia.sin_addr.s_addr==fromAddress.ia.sin_addr.s_addr)
|
||||
{
|
||||
ignore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(likely(!ignore)) {
|
||||
_receiveBuffer->setPosition(bytesRead);
|
||||
|
||||
_receiveBuffer->flip();
|
||||
|
||||
processBuffer(thisTransport, fromAddress, _receiveBuffer.get());
|
||||
}
|
||||
}
|
||||
else if (unlikely(bytesRead == -1)) {
|
||||
|
||||
int socketError = SOCKERRNO;
|
||||
|
||||
// interrupted or timeout
|
||||
if (socketError == SOCK_EINTR ||
|
||||
socketError == EAGAIN || // no alias in libCom
|
||||
// windows times out with this
|
||||
socketError == SOCK_ETIMEDOUT ||
|
||||
socketError == SOCK_EWOULDBLOCK)
|
||||
continue;
|
||||
|
||||
if (socketError == SOCK_ECONNREFUSED || // avoid spurious ECONNREFUSED in Linux
|
||||
socketError == SOCK_ECONNRESET) // or ECONNRESET in Windows
|
||||
continue;
|
||||
|
||||
// log a 'recvfrom' error
|
||||
if(!_closed.get())
|
||||
{
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
LOG(logLevelError, "Socket recvfrom error: %s", errStr);
|
||||
}
|
||||
|
||||
close(false);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
} catch(...) {
|
||||
// TODO: catch all exceptions, and act accordingly
|
||||
close(false);
|
||||
}
|
||||
|
||||
String threadName = "UDP-receive "+inetAddressToString(_bindAddress);
|
||||
/*
|
||||
char threadName[40];
|
||||
epicsThreadGetName(_threadId, threadName, 40);
|
||||
*/
|
||||
LOG(logLevelDebug, "Thread '%s' exiting", threadName.c_str());
|
||||
|
||||
_shutdownEvent.signal();
|
||||
}
|
||||
|
||||
bool BlockingUDPTransport::processBuffer(Transport::shared_pointer const & thisTransport, osiSockAddr& fromAddress, ByteBuffer* receiveBuffer) {
|
||||
|
||||
// handle response(s)
|
||||
while(likely((int)receiveBuffer->getRemaining()>=PVA_MESSAGE_HEADER_SIZE)) {
|
||||
//
|
||||
// read header
|
||||
//
|
||||
|
||||
// first byte is PVA_MAGIC
|
||||
int8 magic = receiveBuffer->getByte();
|
||||
if(unlikely(magic != PVA_MAGIC))
|
||||
return false;
|
||||
|
||||
// second byte version
|
||||
int8 version = receiveBuffer->getByte();
|
||||
|
||||
// only data for UDP
|
||||
int8 flags = receiveBuffer->getByte();
|
||||
if (flags < 0)
|
||||
{
|
||||
// 7-bit set
|
||||
receiveBuffer->setEndianess(EPICS_ENDIAN_BIG);
|
||||
}
|
||||
else
|
||||
{
|
||||
receiveBuffer->setEndianess(EPICS_ENDIAN_LITTLE);
|
||||
}
|
||||
|
||||
// command ID and paylaod
|
||||
int8 command = receiveBuffer->getByte();
|
||||
// TODO check this cast (size_t must be 32-bit)
|
||||
size_t payloadSize = receiveBuffer->getInt();
|
||||
size_t nextRequestPosition = receiveBuffer->getPosition() + payloadSize;
|
||||
|
||||
// payload size check
|
||||
if(unlikely(nextRequestPosition>receiveBuffer->getLimit())) return false;
|
||||
|
||||
// handle
|
||||
_responseHandler->handleResponse(&fromAddress, thisTransport,
|
||||
version, command, payloadSize,
|
||||
_receiveBuffer.get());
|
||||
|
||||
// set position (e.g. in case handler did not read all)
|
||||
receiveBuffer->setPosition(nextRequestPosition);
|
||||
}
|
||||
|
||||
//all ok
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockingUDPTransport::send(ByteBuffer* buffer, const osiSockAddr& address) {
|
||||
|
||||
buffer->flip();
|
||||
int retval = sendto(_channel, buffer->getArray(),
|
||||
buffer->getLimit(), 0, &(address.sa), sizeof(sockaddr));
|
||||
if(unlikely(retval<0))
|
||||
{
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
LOG(logLevelDebug, "Socket sendto error: %s", errStr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockingUDPTransport::send(ByteBuffer* buffer) {
|
||||
if(!_sendAddresses) return false;
|
||||
|
||||
buffer->flip();
|
||||
|
||||
bool allOK = true;
|
||||
for(size_t i = 0; i<_sendAddresses->size(); i++) {
|
||||
int retval = sendto(_channel, buffer->getArray(),
|
||||
buffer->getLimit(), 0, &((*_sendAddresses)[i].sa),
|
||||
sizeof(sockaddr));
|
||||
if(unlikely(retval<0))
|
||||
{
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
LOG(logLevelDebug, "Socket sendto error: %s", errStr);
|
||||
allOK = false;
|
||||
}
|
||||
}
|
||||
|
||||
return allOK;
|
||||
}
|
||||
|
||||
size_t BlockingUDPTransport::getSocketReceiveBufferSize() const {
|
||||
// Get value of the SO_RCVBUF option for this DatagramSocket,
|
||||
// that is the buffer size used by the platform for input on
|
||||
// this DatagramSocket.
|
||||
|
||||
int sockBufSize = -1;
|
||||
osiSocklen_t intLen = sizeof(int);
|
||||
|
||||
int retval = getsockopt(_channel, SOL_SOCKET, SO_RCVBUF, (char *)&sockBufSize, &intLen);
|
||||
if(unlikely(retval<0))
|
||||
{
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
LOG(logLevelError, "Socket getsockopt SO_RCVBUF error: %s", errStr);
|
||||
}
|
||||
|
||||
return (size_t)sockBufSize;
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::threadRunner(void* param) {
|
||||
((BlockingUDPTransport*)param)->processRead();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
104
src/remote/channelSearchManager.h
Normal file
104
src/remote/channelSearchManager.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CHANNELSEARCHMANAGER_H
|
||||
#define CHANNELSEARCHMANAGER_H
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define channelSearchManagerEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <osiSock.h>
|
||||
|
||||
#include <pv/remote.h>
|
||||
|
||||
#ifdef channelSearchManagerEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef channelSearchManagerEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class SearchInstance {
|
||||
public:
|
||||
POINTER_DEFINITIONS(SearchInstance);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~SearchInstance() {};
|
||||
|
||||
virtual pvAccessID getSearchInstanceID() = 0;
|
||||
|
||||
virtual epics::pvData::String getSearchInstanceName() = 0;
|
||||
|
||||
virtual int32_t& getUserValue() = 0;
|
||||
|
||||
/**
|
||||
* Search response from server (channel found).
|
||||
* @param minorRevision server minor PVA revision.
|
||||
* @param serverAddress server address.
|
||||
*/
|
||||
// TODO make serverAddress an URI or similar
|
||||
virtual void searchResponse(int8_t minorRevision, osiSockAddr* serverAddress) = 0;
|
||||
};
|
||||
|
||||
class ChannelSearchManager {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelSearchManager);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~ChannelSearchManager() {};
|
||||
|
||||
/**
|
||||
* Get number of registered channels.
|
||||
* @return number of registered channels.
|
||||
*/
|
||||
virtual int32_t registeredCount() = 0;
|
||||
|
||||
/**
|
||||
* Register channel.
|
||||
* @param channel
|
||||
*/
|
||||
virtual void registerSearchInstance(SearchInstance::shared_pointer const & channel) = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Unregister channel.
|
||||
* @param channel
|
||||
*/
|
||||
virtual void unregisterSearchInstance(SearchInstance::shared_pointer const & channel) = 0;
|
||||
|
||||
/**
|
||||
* Search response from server (channel found).
|
||||
* @param cid client channel ID.
|
||||
* @param seqNo search sequence number.
|
||||
* @param minorRevision server minor PVA revision.
|
||||
* @param serverAddress server address.
|
||||
*/
|
||||
virtual void searchResponse(pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress) = 0;
|
||||
|
||||
/**
|
||||
* New server detected.
|
||||
* Boost searching of all channels.
|
||||
*/
|
||||
virtual void newServerDetected() = 0;
|
||||
|
||||
/**
|
||||
* Cancel.
|
||||
*/
|
||||
virtual void cancel() = 0;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
1619
src/remote/codec.cpp
Normal file
1619
src/remote/codec.cpp
Normal file
File diff suppressed because it is too large
Load Diff
802
src/remote/codec.h
Normal file
802
src/remote/codec.h
Normal file
@@ -0,0 +1,802 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CODEC_H_
|
||||
#define CODEC_H_
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define abstractCodecEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <osdSock.h>
|
||||
#include <osiSock.h>
|
||||
#include <epicsTime.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/timer.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/likely.h>
|
||||
|
||||
#ifdef abstractCodecEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef abstractCodecEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvaConstants.h>
|
||||
#include <pv/remote.h>
|
||||
#include <pv/transportRegistry.h>
|
||||
#include <pv/introspectionRegistry.h>
|
||||
#include <pv/namedLockPattern.h>
|
||||
#include <pv/inetAddressUtil.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
namespace detail {
|
||||
|
||||
// TODO replace mutex with atomic (CAS) operations
|
||||
template<typename T>
|
||||
class AtomicValue
|
||||
{
|
||||
public:
|
||||
AtomicValue(): _value(0) {};
|
||||
|
||||
T getAndSet(T value)
|
||||
{
|
||||
mutex.lock();
|
||||
T tmp = _value; _value = value;
|
||||
mutex.unlock();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
T get() { mutex.lock(); T tmp = _value; mutex.unlock(); return tmp; }
|
||||
|
||||
private:
|
||||
T _value;
|
||||
epics::pvData::Mutex mutex;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
class queue {
|
||||
public:
|
||||
|
||||
queue(void) { }
|
||||
//TODO
|
||||
/*queue(queue const &T) = delete;
|
||||
queue(queue &&T) = delete;
|
||||
queue& operator=(const queue &T) = delete;
|
||||
*/
|
||||
~queue(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool empty(void)
|
||||
{
|
||||
epics::pvData::Lock lock(_queueMutex);
|
||||
return _queue.empty();
|
||||
}
|
||||
|
||||
void clean()
|
||||
{
|
||||
epics::pvData::Lock lock(_queueMutex);
|
||||
_queue.clear();
|
||||
}
|
||||
|
||||
|
||||
void wakeup()
|
||||
{
|
||||
if (!_wakeup.getAndSet(true))
|
||||
{
|
||||
_queueEvent.signal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void put(T const & elem)
|
||||
{
|
||||
{
|
||||
epics::pvData::Lock lock(_queueMutex);
|
||||
_queue.push_back(elem);
|
||||
}
|
||||
|
||||
_queueEvent.signal();
|
||||
}
|
||||
|
||||
|
||||
T take(int timeOut)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
|
||||
bool isEmpty = empty();
|
||||
|
||||
if (isEmpty)
|
||||
{
|
||||
|
||||
if (timeOut < 0) {
|
||||
return T();
|
||||
}
|
||||
|
||||
while (isEmpty)
|
||||
{
|
||||
|
||||
if (timeOut == 0) {
|
||||
_queueEvent.wait();
|
||||
}
|
||||
else {
|
||||
_queueEvent.wait(timeOut);
|
||||
}
|
||||
|
||||
isEmpty = empty();
|
||||
if (isEmpty)
|
||||
{
|
||||
if (timeOut > 0) { // TODO spurious wakeup, but not critical
|
||||
return T();
|
||||
}
|
||||
else // if (timeout == 0) cannot be negative
|
||||
{
|
||||
if (_wakeup.getAndSet(false)) {
|
||||
return T();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
epics::pvData::Lock lock(_queueMutex);
|
||||
T sender = _queue.front();
|
||||
_queue.pop_front();
|
||||
return sender;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::deque<T> _queue;
|
||||
epics::pvData::Event _queueEvent;
|
||||
epics::pvData::Mutex _queueMutex;
|
||||
AtomicValue<bool> _wakeup;
|
||||
epics::pvData::Mutex _stdMutex;
|
||||
};
|
||||
|
||||
|
||||
class epicsShareClass io_exception: public std::runtime_error {
|
||||
public:
|
||||
explicit io_exception(const std::string &s): std::runtime_error(s) {}
|
||||
};
|
||||
|
||||
|
||||
class epicsShareClass invalid_data_stream_exception: public std::runtime_error {
|
||||
public:
|
||||
explicit invalid_data_stream_exception(
|
||||
const std::string &s): std::runtime_error(s) {}
|
||||
};
|
||||
|
||||
|
||||
class epicsShareClass connection_closed_exception: public std::runtime_error {
|
||||
public:
|
||||
explicit connection_closed_exception(const std::string &s): std::runtime_error(s) {}
|
||||
};
|
||||
|
||||
|
||||
enum ReadMode { NORMAL, SPLIT, SEGMENTED };
|
||||
|
||||
enum WriteMode { PROCESS_SEND_QUEUE, WAIT_FOR_READY_SIGNAL };
|
||||
|
||||
|
||||
class epicsShareClass AbstractCodec :
|
||||
public TransportSendControl,
|
||||
public Transport
|
||||
{
|
||||
public:
|
||||
|
||||
static const std::size_t MAX_MESSAGE_PROCESS;
|
||||
static const std::size_t MAX_MESSAGE_SEND;
|
||||
static const std::size_t MAX_ENSURE_SIZE;
|
||||
static const std::size_t MAX_ENSURE_DATA_SIZE;
|
||||
static const std::size_t MAX_ENSURE_BUFFER_SIZE;
|
||||
static const std::size_t MAX_ENSURE_DATA_BUFFER_SIZE;
|
||||
|
||||
AbstractCodec(
|
||||
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & receiveBuffer,
|
||||
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & sendBuffer,
|
||||
int32_t socketSendBufferSize,
|
||||
bool blockingProcessQueue);
|
||||
|
||||
virtual void processControlMessage() = 0;
|
||||
virtual void processApplicationMessage() = 0;
|
||||
virtual const osiSockAddr* getLastReadBufferSocketAddress() = 0;
|
||||
virtual void invalidDataStreamHandler() = 0;
|
||||
virtual void readPollOne()=0;
|
||||
virtual void writePollOne() = 0;
|
||||
virtual void scheduleSend() = 0;
|
||||
virtual void sendCompleted() = 0;
|
||||
virtual bool terminated() = 0;
|
||||
virtual int write(epics::pvData::ByteBuffer* src) = 0;
|
||||
virtual int read(epics::pvData::ByteBuffer* dst) = 0;
|
||||
virtual bool isOpen() = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
|
||||
virtual ~AbstractCodec()
|
||||
{
|
||||
}
|
||||
|
||||
void alignBuffer(std::size_t alignment);
|
||||
void ensureData(std::size_t size);
|
||||
void alignData(std::size_t alignment);
|
||||
void startMessage(
|
||||
epics::pvData::int8 command,
|
||||
std::size_t ensureCapacity);
|
||||
void putControlMessage(
|
||||
epics::pvData::int8 command,
|
||||
epics::pvData::int32 data);
|
||||
void endMessage();
|
||||
void ensureBuffer(std::size_t size);
|
||||
void flushSerializeBuffer();
|
||||
void flush(bool lastMessageCompleted);
|
||||
void processWrite();
|
||||
void processRead();
|
||||
void processSendQueue();
|
||||
void clearSendQueue();
|
||||
void enqueueSendRequest(TransportSender::shared_pointer const & sender);
|
||||
void enqueueSendRequest(TransportSender::shared_pointer const & sender,
|
||||
std::size_t requiredBufferSize);
|
||||
void setSenderThread();
|
||||
void setRecipient(osiSockAddr const & sendTo);
|
||||
void setByteOrder(int byteOrder);
|
||||
|
||||
static std::size_t alignedValue(std::size_t value, std::size_t alignment);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void sendBufferFull(int tries) = 0;
|
||||
void send(epics::pvData::ByteBuffer *buffer);
|
||||
|
||||
|
||||
ReadMode _readMode;
|
||||
int8_t _version;
|
||||
int8_t _flags;
|
||||
int8_t _command;
|
||||
int32_t _payloadSize; // TODO why not size_t?
|
||||
epics::pvData::int32 _remoteTransportSocketReceiveBufferSize;
|
||||
int64_t _totalBytesSent;
|
||||
bool _blockingProcessQueue;
|
||||
//TODO initialize union
|
||||
osiSockAddr _sendTo;
|
||||
epicsThreadId _senderThread;
|
||||
WriteMode _writeMode;
|
||||
bool _writeOpReady;
|
||||
bool _lowLatency;
|
||||
|
||||
std::tr1::shared_ptr<epics::pvData::ByteBuffer> _socketBuffer;
|
||||
std::tr1::shared_ptr<epics::pvData::ByteBuffer> _sendBuffer;
|
||||
|
||||
queue<TransportSender::shared_pointer> _sendQueue;
|
||||
|
||||
private:
|
||||
|
||||
void processHeader();
|
||||
void processReadNormal();
|
||||
void postProcessApplicationMessage();
|
||||
void processReadSegmented();
|
||||
bool readToBuffer(std::size_t requiredBytes, bool persistent);
|
||||
void endMessage(bool hasMoreSegments);
|
||||
void processSender(
|
||||
epics::pvAccess::TransportSender::shared_pointer const & sender);
|
||||
|
||||
std::size_t _storedPayloadSize;
|
||||
std::size_t _storedPosition;
|
||||
std::size_t _storedLimit;
|
||||
std::size_t _startPosition;
|
||||
|
||||
std::size_t _maxSendPayloadSize;
|
||||
std::size_t _lastMessageStartPosition;
|
||||
std::size_t _lastSegmentedMessageType;
|
||||
int8_t _lastSegmentedMessageCommand;
|
||||
std::size_t _nextMessagePayloadOffset;
|
||||
|
||||
epics::pvData::int8 _byteOrderFlag;
|
||||
int32_t _socketSendBufferSize;
|
||||
};
|
||||
|
||||
|
||||
class epicsShareClass BlockingAbstractCodec:
|
||||
public AbstractCodec,
|
||||
public std::tr1::enable_shared_from_this<BlockingAbstractCodec>
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
POINTER_DEFINITIONS(BlockingAbstractCodec);
|
||||
|
||||
BlockingAbstractCodec(
|
||||
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & receiveBuffer,
|
||||
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & sendBuffer,
|
||||
int32_t socketSendBufferSize):
|
||||
AbstractCodec(receiveBuffer, sendBuffer, socketSendBufferSize, true),
|
||||
_readThread(0), _sendThread(0) { _isOpen.getAndSet(true);}
|
||||
|
||||
void readPollOne();
|
||||
void writePollOne();
|
||||
void scheduleSend() {}
|
||||
void sendCompleted() {}
|
||||
void close();
|
||||
bool terminated();
|
||||
bool isOpen();
|
||||
void start();
|
||||
|
||||
static void receiveThread(void* param);
|
||||
static void sendThread(void* param);
|
||||
|
||||
protected:
|
||||
void sendBufferFull(int tries);
|
||||
virtual void internalDestroy() = 0;
|
||||
|
||||
/**
|
||||
* Called to any resources just before closing transport
|
||||
* @param[in] force flag indicating if forced (e.g. forced
|
||||
* disconnect) is required
|
||||
*/
|
||||
virtual void internalClose(bool force);
|
||||
|
||||
/**
|
||||
* Called to any resources just after closing transport and without any locks held on transport
|
||||
* @param[in] force flag indicating if forced (e.g. forced
|
||||
* disconnect) is required
|
||||
*/
|
||||
virtual void internalPostClose(bool force);
|
||||
|
||||
private:
|
||||
AtomicValue<bool> _isOpen;
|
||||
volatile epicsThreadId _readThread;
|
||||
volatile epicsThreadId _sendThread;
|
||||
epics::pvData::Event _shutdownEvent;
|
||||
};
|
||||
|
||||
|
||||
class epicsShareClass BlockingSocketAbstractCodec:
|
||||
public BlockingAbstractCodec
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
BlockingSocketAbstractCodec(
|
||||
SOCKET channel,
|
||||
int32_t sendBufferSize,
|
||||
int32_t receiveBufferSize);
|
||||
|
||||
int read(epics::pvData::ByteBuffer* dst);
|
||||
int write(epics::pvData::ByteBuffer* src);
|
||||
const osiSockAddr* getLastReadBufferSocketAddress() { return &_socketAddress; }
|
||||
void invalidDataStreamHandler();
|
||||
std::size_t getSocketReceiveBufferSize() const;
|
||||
|
||||
protected:
|
||||
|
||||
void internalDestroy();
|
||||
|
||||
SOCKET _channel;
|
||||
osiSockAddr _socketAddress;
|
||||
};
|
||||
|
||||
|
||||
class BlockingTCPTransportCodec :
|
||||
public BlockingSocketAbstractCodec
|
||||
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
epics::pvData::String getType() const {
|
||||
return epics::pvData::String("TCP");
|
||||
}
|
||||
|
||||
|
||||
void internalDestroy() {
|
||||
BlockingSocketAbstractCodec::internalDestroy();
|
||||
Transport::shared_pointer thisSharedPtr = this->shared_from_this();
|
||||
_context->getTransportRegistry()->remove(thisSharedPtr);
|
||||
}
|
||||
|
||||
|
||||
void changedTransport() {}
|
||||
|
||||
|
||||
void processControlMessage() {
|
||||
if (_command == 2)
|
||||
{
|
||||
// check 7-th bit
|
||||
setByteOrder(_flags < 0 ? EPICS_ENDIAN_BIG : EPICS_ENDIAN_LITTLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void processApplicationMessage() {
|
||||
_responseHandler->handleResponse(&_socketAddress, shared_from_this(),
|
||||
_version, _command, _payloadSize, _socketBuffer.get());
|
||||
}
|
||||
|
||||
|
||||
const osiSockAddr* getRemoteAddress() const {
|
||||
return &_socketAddress;
|
||||
}
|
||||
|
||||
|
||||
epics::pvData::int8 getRevision() const {
|
||||
return PVA_PROTOCOL_REVISION;
|
||||
}
|
||||
|
||||
|
||||
std::size_t getReceiveBufferSize() const {
|
||||
return _socketBuffer->getSize();
|
||||
}
|
||||
|
||||
|
||||
epics::pvData::int16 getPriority() const {
|
||||
return _priority;
|
||||
}
|
||||
|
||||
|
||||
void setRemoteRevision(epics::pvData::int8 revision) {
|
||||
_remoteTransportRevision = revision;
|
||||
}
|
||||
|
||||
|
||||
void setRemoteTransportReceiveBufferSize(
|
||||
std::size_t remoteTransportReceiveBufferSize) {
|
||||
_remoteTransportReceiveBufferSize = remoteTransportReceiveBufferSize;
|
||||
}
|
||||
|
||||
|
||||
void setRemoteTransportSocketReceiveBufferSize(
|
||||
std::size_t socketReceiveBufferSize) {
|
||||
_remoteTransportSocketReceiveBufferSize = socketReceiveBufferSize;
|
||||
}
|
||||
|
||||
|
||||
std::tr1::shared_ptr<const epics::pvData::Field>
|
||||
cachedDeserialize(epics::pvData::ByteBuffer* buffer)
|
||||
{
|
||||
return _incomingIR.deserialize(buffer, this);
|
||||
}
|
||||
|
||||
|
||||
void cachedSerialize(
|
||||
const std::tr1::shared_ptr<const epics::pvData::Field>& field,
|
||||
epics::pvData::ByteBuffer* buffer)
|
||||
{
|
||||
_outgoingIR.serialize(field, buffer, this);
|
||||
}
|
||||
|
||||
|
||||
bool directSerialize(
|
||||
epics::pvData::ByteBuffer * /*existingBuffer*/,
|
||||
const char* /*toSerialize*/,
|
||||
std::size_t /*elementCount*/, std::size_t /*elementSize*/)
|
||||
{
|
||||
// TODO !!!!
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool directDeserialize(epics::pvData::ByteBuffer * /*existingBuffer*/,
|
||||
char* /*deserializeTo*/,
|
||||
std::size_t /*elementCount*/, std::size_t /*elementSize*/) {
|
||||
// TODO !!!
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void flushSendQueue() { };
|
||||
|
||||
|
||||
bool isClosed() {
|
||||
return !isOpen();
|
||||
}
|
||||
|
||||
|
||||
void activate() {
|
||||
Transport::shared_pointer thisSharedPtr = shared_from_this();
|
||||
_context->getTransportRegistry()->put(thisSharedPtr);
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
BlockingTCPTransportCodec(
|
||||
Context::shared_pointer const & context,
|
||||
SOCKET channel,
|
||||
std::auto_ptr<ResponseHandler>& responseHandler,
|
||||
int32_t sendBufferSize,
|
||||
int32_t receiveBufferSize,
|
||||
epics::pvData::int16 priority
|
||||
):
|
||||
BlockingSocketAbstractCodec(channel, sendBufferSize, receiveBufferSize),
|
||||
_context(context), _responseHandler(responseHandler),
|
||||
_remoteTransportReceiveBufferSize(MAX_TCP_RECV),
|
||||
_remoteTransportRevision(0), _priority(priority)
|
||||
{
|
||||
}
|
||||
|
||||
Context::shared_pointer _context;
|
||||
|
||||
IntrospectionRegistry _incomingIR;
|
||||
IntrospectionRegistry _outgoingIR;
|
||||
|
||||
private:
|
||||
|
||||
std::auto_ptr<ResponseHandler> _responseHandler;
|
||||
size_t _remoteTransportReceiveBufferSize;
|
||||
epics::pvData::int8 _remoteTransportRevision;
|
||||
epics::pvData::int16 _priority;
|
||||
};
|
||||
|
||||
|
||||
class epicsShareClass BlockingServerTCPTransportCodec :
|
||||
public BlockingTCPTransportCodec,
|
||||
public ChannelHostingTransport,
|
||||
public TransportSender {
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(BlockingServerTCPTransportCodec);
|
||||
|
||||
protected:
|
||||
BlockingServerTCPTransportCodec(
|
||||
Context::shared_pointer const & context,
|
||||
SOCKET channel,
|
||||
std::auto_ptr<ResponseHandler>& responseHandler,
|
||||
int32_t sendBufferSize,
|
||||
int32_t receiveBufferSize );
|
||||
|
||||
public:
|
||||
static shared_pointer create(
|
||||
Context::shared_pointer const & context,
|
||||
SOCKET channel,
|
||||
std::auto_ptr<ResponseHandler>& responseHandler,
|
||||
int sendBufferSize,
|
||||
int receiveBufferSize)
|
||||
{
|
||||
shared_pointer thisPointer(
|
||||
new BlockingServerTCPTransportCodec(
|
||||
context, channel, responseHandler,
|
||||
sendBufferSize, receiveBufferSize)
|
||||
);
|
||||
thisPointer->activate();
|
||||
return thisPointer;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
bool acquire(std::tr1::shared_ptr<TransportClient> const & client)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void release(pvAccessID /*clientId*/) {}
|
||||
|
||||
pvAccessID preallocateChannelSID();
|
||||
|
||||
void depreallocateChannelSID(pvAccessID /*sid*/) {
|
||||
// noop
|
||||
}
|
||||
|
||||
void registerChannel(
|
||||
pvAccessID sid,
|
||||
ServerChannel::shared_pointer const & channel);
|
||||
|
||||
void unregisterChannel(pvAccessID sid);
|
||||
|
||||
ServerChannel::shared_pointer getChannel(pvAccessID sid);
|
||||
|
||||
int getChannelCount();
|
||||
|
||||
epics::pvData::PVField::shared_pointer getSecurityToken() {
|
||||
return epics::pvData::PVField::shared_pointer();
|
||||
}
|
||||
|
||||
void lock() {
|
||||
// noop
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
// noop
|
||||
}
|
||||
|
||||
bool verify(epics::pvData::int32 timeoutMs) {
|
||||
TransportSender::shared_pointer transportSender =
|
||||
std::tr1::dynamic_pointer_cast<TransportSender>(shared_from_this());
|
||||
enqueueSendRequest(transportSender);
|
||||
verified();
|
||||
return true;
|
||||
}
|
||||
|
||||
void verified() {
|
||||
}
|
||||
|
||||
void aliveNotification() {
|
||||
// noop on server-side
|
||||
}
|
||||
|
||||
void send(epics::pvData::ByteBuffer* buffer,
|
||||
TransportSendControl* control);
|
||||
|
||||
virtual ~BlockingServerTCPTransportCodec();
|
||||
|
||||
protected:
|
||||
|
||||
void destroyAllChannels();
|
||||
virtual void internalClose(bool force);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Last SID cache.
|
||||
*/
|
||||
pvAccessID _lastChannelSID;
|
||||
|
||||
/**
|
||||
* Channel table (SID -> channel mapping).
|
||||
*/
|
||||
std::map<pvAccessID, ServerChannel::shared_pointer> _channels;
|
||||
|
||||
epics::pvData::Mutex _channelsMutex;
|
||||
|
||||
};
|
||||
|
||||
class epicsShareClass BlockingClientTCPTransportCodec :
|
||||
public BlockingTCPTransportCodec,
|
||||
public TransportSender,
|
||||
public epics::pvData::TimerCallback {
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(BlockingClientTCPTransportCodec);
|
||||
|
||||
protected:
|
||||
BlockingClientTCPTransportCodec(
|
||||
Context::shared_pointer const & context,
|
||||
SOCKET channel,
|
||||
std::auto_ptr<ResponseHandler>& responseHandler,
|
||||
int32_t sendBufferSize,
|
||||
int32_t receiveBufferSize,
|
||||
TransportClient::shared_pointer const & client,
|
||||
epics::pvData::int8 remoteTransportRevision,
|
||||
float beaconInterval,
|
||||
int16_t priority);
|
||||
|
||||
public:
|
||||
static shared_pointer create(
|
||||
Context::shared_pointer const & context,
|
||||
SOCKET channel,
|
||||
std::auto_ptr<ResponseHandler>& responseHandler,
|
||||
int32_t sendBufferSize,
|
||||
int32_t receiveBufferSize,
|
||||
TransportClient::shared_pointer const & client,
|
||||
int8_t remoteTransportRevision,
|
||||
float beaconInterval,
|
||||
int16_t priority )
|
||||
{
|
||||
shared_pointer thisPointer(
|
||||
new BlockingClientTCPTransportCodec(
|
||||
context, channel, responseHandler,
|
||||
sendBufferSize, receiveBufferSize,
|
||||
client, remoteTransportRevision,
|
||||
beaconInterval, priority)
|
||||
);
|
||||
thisPointer->activate();
|
||||
return thisPointer;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void start();
|
||||
|
||||
virtual ~BlockingClientTCPTransportCodec();
|
||||
|
||||
virtual void timerStopped() {
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual void callback();
|
||||
|
||||
bool acquire(TransportClient::shared_pointer const & client);
|
||||
|
||||
void release(pvAccessID clientId);
|
||||
|
||||
void changedTransport();
|
||||
|
||||
void lock() {
|
||||
// noop
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
// noop
|
||||
}
|
||||
|
||||
bool verify(epics::pvData::int32 timeoutMs);
|
||||
|
||||
void verified();
|
||||
|
||||
void aliveNotification();
|
||||
|
||||
void send(epics::pvData::ByteBuffer* buffer,
|
||||
TransportSendControl* control);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void internalClose(bool force);
|
||||
virtual void internalPostClose(bool force);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Owners (users) of the transport.
|
||||
*/
|
||||
// TODO consider using TR1 hash map
|
||||
typedef std::map<pvAccessID, TransportClient::weak_pointer> TransportClientMap_t;
|
||||
TransportClientMap_t _owners;
|
||||
|
||||
/**
|
||||
* Connection timeout (no-traffic) flag.
|
||||
*/
|
||||
double _connectionTimeout;
|
||||
|
||||
/**
|
||||
* Unresponsive transport flag.
|
||||
*/
|
||||
bool _unresponsiveTransport;
|
||||
|
||||
/**
|
||||
* Timestamp of last "live" event on this transport.
|
||||
*/
|
||||
epicsTimeStamp _aliveTimestamp;
|
||||
|
||||
bool _verifyOrEcho;
|
||||
|
||||
/**
|
||||
* Unresponsive transport notify.
|
||||
*/
|
||||
void unresponsiveTransport();
|
||||
|
||||
/**
|
||||
* Notifies clients about disconnect.
|
||||
*/
|
||||
void closedNotifyClients();
|
||||
|
||||
/**
|
||||
* Responsive transport notify.
|
||||
*/
|
||||
void responsiveTransport();
|
||||
|
||||
|
||||
epics::pvData::Mutex _mutex;
|
||||
|
||||
bool _verified;
|
||||
epics::pvData::Mutex _verifiedMutex;
|
||||
epics::pvData::Event _verifiedEvent;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CODEC_H_ */
|
||||
612
src/remote/remote.h
Normal file
612
src/remote/remote.h
Normal file
@@ -0,0 +1,612 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef REMOTE_H_
|
||||
#define REMOTE_H_
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define remoteEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <osiSock.h>
|
||||
#include <osdSock.h>
|
||||
|
||||
#include <pv/serialize.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/timer.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#ifdef remoteEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef remoteEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvaConstants.h>
|
||||
#include <pv/configuration.h>
|
||||
|
||||
/// TODO only here because of the Lockable
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
#define PVACCESS_REFCOUNT_MONITOR_DEFINE(name)
|
||||
#define PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(name)
|
||||
#define PVACCESS_REFCOUNT_MONITOR_DESTRUCT(name)
|
||||
|
||||
class TransportRegistry;
|
||||
|
||||
enum QoS {
|
||||
/**
|
||||
* Default behavior.
|
||||
*/
|
||||
QOS_DEFAULT = 0x00,
|
||||
/**
|
||||
* Require reply (acknowledgment for reliable operation).
|
||||
*/
|
||||
QOS_REPLY_REQUIRED = 0x01,
|
||||
/**
|
||||
* Best-effort option (no reply).
|
||||
*/
|
||||
QOS_BESY_EFFORT = 0x02,
|
||||
/**
|
||||
* Process option.
|
||||
*/
|
||||
QOS_PROCESS = 0x04,
|
||||
/**
|
||||
* Initialize option.
|
||||
*/
|
||||
QOS_INIT = 0x08,
|
||||
/**
|
||||
* Destroy option.
|
||||
*/
|
||||
QOS_DESTROY = 0x10,
|
||||
/**
|
||||
* Share data option.
|
||||
*/
|
||||
QOS_SHARE = 0x20,
|
||||
/**
|
||||
* Get.
|
||||
*/
|
||||
QOS_GET = 0x40,
|
||||
/**
|
||||
* Get-put.
|
||||
*/
|
||||
QOS_GET_PUT = 0x80
|
||||
};
|
||||
|
||||
typedef epics::pvData::int32 pvAccessID;
|
||||
|
||||
enum ApplicationCommands {
|
||||
CMD_BEACON = 0,
|
||||
CMD_CONNECTION_VALIDATION = 1,
|
||||
CMD_ECHO = 2,
|
||||
CMD_SEARCH = 3,
|
||||
CMD_SEARCH_RESPONSE = 4,
|
||||
CMD_INTROSPECTION_SEARCH = 5,
|
||||
CMD_INTROSPECTION_SEARCH_RESPONSE = 6,
|
||||
CMD_CREATE_CHANNEL = 7,
|
||||
CMD_DESTROY_CHANNEL = 8,
|
||||
CMD_RESERVED0 = 9,
|
||||
CMD_GET = 10,
|
||||
CMD_PUT = 11,
|
||||
CMD_PUT_GET = 12,
|
||||
CMD_MONITOR = 13,
|
||||
CMD_ARRAY = 14,
|
||||
CMD_CANCEL_REQUEST = 15,
|
||||
CMD_PROCESS = 16,
|
||||
CMD_GET_FIELD = 17,
|
||||
CMD_MESSAGE = 18,
|
||||
CMD_MULTIPLE_DATA = 19,
|
||||
CMD_RPC = 20
|
||||
};
|
||||
|
||||
enum ControlCommands {
|
||||
CMD_SET_MARKER = 0,
|
||||
CMD_ACK_MARKER = 1,
|
||||
CMD_SET_ENDIANESS = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface defining transport send control.
|
||||
*/
|
||||
class TransportSendControl : public epics::pvData::SerializableControl {
|
||||
public:
|
||||
POINTER_DEFINITIONS(TransportSendControl);
|
||||
|
||||
virtual ~TransportSendControl() {}
|
||||
|
||||
virtual void startMessage(epics::pvData::int8 command, std::size_t ensureCapacity) = 0;
|
||||
virtual void endMessage() = 0;
|
||||
|
||||
virtual void flush(bool lastMessageCompleted) = 0;
|
||||
|
||||
virtual void setRecipient(osiSockAddr const & sendTo) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface defining transport sender (instance sending data over transport).
|
||||
*/
|
||||
class TransportSender : public Lockable {
|
||||
public:
|
||||
POINTER_DEFINITIONS(TransportSender);
|
||||
|
||||
virtual ~TransportSender() {}
|
||||
|
||||
/**
|
||||
* Called by transport.
|
||||
* By this call transport gives callee ownership over the buffer.
|
||||
* Calls on <code>TransportSendControl</code> instance must be made from
|
||||
* calling thread. Moreover, ownership is valid only for the time of call
|
||||
* of this method.
|
||||
* NOTE: these limitations allow efficient implementation.
|
||||
*/
|
||||
virtual void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control) = 0;
|
||||
};
|
||||
|
||||
class TransportClient;
|
||||
|
||||
/**
|
||||
* Interface defining transport (connection).
|
||||
*/
|
||||
class Transport : public epics::pvData::DeserializableControl {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Transport);
|
||||
|
||||
virtual ~Transport() {}
|
||||
|
||||
/**
|
||||
* Acquires transport.
|
||||
* @param client client (channel) acquiring the transport
|
||||
* @return <code>true</code> if transport was granted, <code>false</code> otherwise.
|
||||
*/
|
||||
//virtual bool acquire(TransportClient::shared_pointer const & client) = 0;
|
||||
virtual bool acquire(std::tr1::shared_ptr<TransportClient> const & client) = 0;
|
||||
|
||||
/**
|
||||
* Releases transport.
|
||||
* @param client client (channel) releasing the transport
|
||||
*/
|
||||
virtual void release(pvAccessID clientId) = 0;
|
||||
//virtual void release(TransportClient::shared_pointer const & client) = 0;
|
||||
|
||||
/**
|
||||
* Get protocol type (tcp, udp, ssl, etc.).
|
||||
* @return protocol type.
|
||||
*/
|
||||
virtual epics::pvData::String getType() const = 0;
|
||||
|
||||
/**
|
||||
* Get remote address.
|
||||
* @return remote address, can be null.
|
||||
*/
|
||||
virtual const osiSockAddr* getRemoteAddress() const = 0;
|
||||
|
||||
// TODO getContext?
|
||||
|
||||
/**
|
||||
* Transport protocol minor revision.
|
||||
* @return protocol minor revision.
|
||||
*/
|
||||
virtual epics::pvData::int8 getRevision() const = 0;
|
||||
|
||||
/**
|
||||
* Get receive buffer size.
|
||||
* @return receive buffer size.
|
||||
*/
|
||||
virtual std::size_t getReceiveBufferSize() const = 0;
|
||||
|
||||
/**
|
||||
* Get socket receive buffer size.
|
||||
* @return socket receive buffer size.
|
||||
*/
|
||||
virtual std::size_t getSocketReceiveBufferSize() const = 0;
|
||||
|
||||
/**
|
||||
* Transport priority.
|
||||
* @return protocol priority.
|
||||
*/
|
||||
virtual epics::pvData::int16 getPriority() const = 0;
|
||||
|
||||
/**
|
||||
* Set remote transport protocol revision.
|
||||
* @param revision protocol revision.
|
||||
*/
|
||||
virtual void setRemoteRevision(epics::pvData::int8 revision) = 0;
|
||||
|
||||
/**
|
||||
* Set remote transport receive buffer size.
|
||||
* @param receiveBufferSize receive buffer size.
|
||||
*/
|
||||
virtual void setRemoteTransportReceiveBufferSize(std::size_t receiveBufferSize) = 0;
|
||||
|
||||
/**
|
||||
* Set remote transport socket receive buffer size.
|
||||
* @param socketReceiveBufferSize remote socket receive buffer size.
|
||||
*/
|
||||
virtual void setRemoteTransportSocketReceiveBufferSize(std::size_t socketReceiveBufferSize) = 0;
|
||||
|
||||
/**
|
||||
* Set byte order.
|
||||
* @param byteOrder byte order to set.
|
||||
*/
|
||||
// TODO enum
|
||||
virtual void setByteOrder(int byteOrder) = 0;
|
||||
|
||||
/**
|
||||
* Notification that transport has changed.
|
||||
*/
|
||||
virtual void changedTransport() = 0;
|
||||
|
||||
/**
|
||||
* Enqueue send request.
|
||||
* @param sender
|
||||
*/
|
||||
virtual void enqueueSendRequest(TransportSender::shared_pointer const & sender) = 0;
|
||||
|
||||
/**
|
||||
* Flush send queue (sent messages).
|
||||
*/
|
||||
virtual void flushSendQueue() = 0;
|
||||
|
||||
/**
|
||||
* Notify transport that it is has been verified.
|
||||
*/
|
||||
virtual void verified() = 0;
|
||||
|
||||
/**
|
||||
* Waits (if needed) until transport is verified, i.e. verified() method is being called.
|
||||
* @param timeoutMs timeout to wait for verification, infinite if 0.
|
||||
*/
|
||||
virtual bool verify(epics::pvData::int32 timeoutMs) = 0;
|
||||
|
||||
/**
|
||||
* Notification transport that is still alive.
|
||||
*/
|
||||
virtual void aliveNotification() = 0;
|
||||
|
||||
/**
|
||||
* Close transport.
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
/**
|
||||
* Check connection status.
|
||||
* @return <code>true</code> if connected.
|
||||
*/
|
||||
virtual bool isClosed() = 0;
|
||||
};
|
||||
|
||||
class Channel;
|
||||
|
||||
/**
|
||||
* Not public IF, used by Transports, etc.
|
||||
*/
|
||||
class Context {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Context);
|
||||
|
||||
virtual ~Context() {}
|
||||
|
||||
virtual epics::pvData::Timer::shared_pointer getTimer() = 0;
|
||||
|
||||
//virtual TransportRegistry::shared_pointer getTransportRegistry() = 0;
|
||||
virtual std::tr1::shared_ptr<TransportRegistry> getTransportRegistry() = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
virtual Configuration::shared_pointer getConfiguration() = 0;
|
||||
|
||||
|
||||
|
||||
///
|
||||
/// due to ClientContextImpl
|
||||
///
|
||||
|
||||
virtual void newServerDetected() = 0;
|
||||
|
||||
virtual std::tr1::shared_ptr<Channel> getChannel(pvAccessID id) = 0;
|
||||
virtual Transport::shared_pointer getSearchTransport() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface defining response handler.
|
||||
*/
|
||||
class ResponseHandler {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ResponseHandler);
|
||||
|
||||
virtual ~ResponseHandler() {}
|
||||
|
||||
/**
|
||||
* Handle response.
|
||||
* @param[in] responseFrom remote address of the responder, <code>0</code> if unknown.
|
||||
* @param[in] transport response source transport.
|
||||
* @param[in] version message version.
|
||||
* @param[in] payloadSize size of this message data available in the <code>payloadBuffer</code>.
|
||||
* @param[in] payloadBuffer message payload data.
|
||||
* Note that this might not be the only message in the buffer.
|
||||
* Code must not manipulate buffer.
|
||||
*/
|
||||
virtual void
|
||||
handleResponse(osiSockAddr* responseFrom, Transport::shared_pointer const & transport,
|
||||
epics::pvData::int8 version, epics::pvData::int8 command, std::size_t payloadSize,
|
||||
epics::pvData::ByteBuffer* payloadBuffer) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base (abstract) channel access response handler.
|
||||
*/
|
||||
class AbstractResponseHandler : public ResponseHandler {
|
||||
public:
|
||||
/**
|
||||
* @param description
|
||||
*/
|
||||
AbstractResponseHandler(Context* context, epics::pvData::String description) :
|
||||
_description(description),
|
||||
_debug(context->getConfiguration()->getPropertyAsBoolean(PVACCESS_DEBUG, false)) {
|
||||
}
|
||||
|
||||
virtual ~AbstractResponseHandler() {}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom, Transport::shared_pointer const & transport,
|
||||
epics::pvData::int8 version, epics::pvData::int8 command, std::size_t payloadSize,
|
||||
epics::pvData::ByteBuffer* payloadBuffer);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Response hanlder description.
|
||||
*/
|
||||
epics::pvData::String _description;
|
||||
|
||||
/**
|
||||
* Debug flag.
|
||||
*/
|
||||
bool _debug;
|
||||
};
|
||||
|
||||
/**
|
||||
* Client (user) of the transport.
|
||||
*/
|
||||
class TransportClient {
|
||||
public:
|
||||
POINTER_DEFINITIONS(TransportClient);
|
||||
|
||||
virtual ~TransportClient() {
|
||||
}
|
||||
|
||||
// ID used to allow fast/efficient lookup
|
||||
virtual pvAccessID getID() = 0;
|
||||
|
||||
/**
|
||||
* Notification of unresponsive transport (e.g. no heartbeat detected) .
|
||||
*/
|
||||
virtual void transportUnresponsive() = 0;
|
||||
|
||||
/**
|
||||
* Notification of responsive transport (e.g. heartbeat detected again),
|
||||
* called to discard <code>transportUnresponsive</code> notification.
|
||||
* @param transport responsive transport.
|
||||
*/
|
||||
virtual void transportResponsive(Transport::shared_pointer const & transport) = 0;
|
||||
|
||||
/**
|
||||
* Notification of network change (server restarted).
|
||||
*/
|
||||
virtual void transportChanged() = 0;
|
||||
|
||||
/**
|
||||
* Notification of forcefully closed transport.
|
||||
*/
|
||||
virtual void transportClosed() = 0;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface defining socket connector (Connector-Transport pattern).
|
||||
*/
|
||||
class Connector {
|
||||
public:
|
||||
virtual ~Connector() {}
|
||||
|
||||
/**
|
||||
* Connect.
|
||||
* @param[in] client client requesting connection (transport).
|
||||
* @param[in] address address of the server.
|
||||
* @param[in] responseHandler reponse handler.
|
||||
* @param[in] transportRevision transport revision to be used.
|
||||
* @param[in] priority process priority.
|
||||
* @return transport instance.
|
||||
*/
|
||||
virtual Transport::shared_pointer connect(TransportClient::shared_pointer const & client,
|
||||
std::auto_ptr<ResponseHandler>& responseHandler, osiSockAddr& address,
|
||||
epics::pvData::int8 transportRevision, epics::pvData::int16 priority) = 0;
|
||||
|
||||
};
|
||||
|
||||
class ServerChannel {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ServerChannel);
|
||||
|
||||
virtual ~ServerChannel() {}
|
||||
/**
|
||||
* Get channel SID.
|
||||
* @return channel SID.
|
||||
*/
|
||||
virtual pvAccessID getSID() const = 0;
|
||||
|
||||
/**
|
||||
* Destroy server channel.
|
||||
* This method MUST BE called if overriden.
|
||||
*/
|
||||
virtual void destroy() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface defining a transport that hosts server channels.
|
||||
*/
|
||||
class ChannelHostingTransport {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelHostingTransport);
|
||||
|
||||
virtual ~ChannelHostingTransport() {}
|
||||
|
||||
/**
|
||||
* Get security token.
|
||||
* @return security token, can be <code>null</code>.
|
||||
*/
|
||||
virtual epics::pvData::PVField::shared_pointer getSecurityToken() = 0;
|
||||
|
||||
/**
|
||||
* Preallocate new channel SID.
|
||||
* @return new channel server id (SID).
|
||||
*/
|
||||
virtual pvAccessID preallocateChannelSID() = 0;
|
||||
|
||||
/**
|
||||
* De-preallocate new channel SID.
|
||||
* @param sid preallocated channel SID.
|
||||
*/
|
||||
virtual void depreallocateChannelSID(pvAccessID sid) = 0;
|
||||
|
||||
/**
|
||||
* Register a new channel.
|
||||
* @param sid preallocated channel SID.
|
||||
* @param channel channel to register.
|
||||
*/
|
||||
virtual void registerChannel(pvAccessID sid, ServerChannel::shared_pointer const & channel) =0;
|
||||
|
||||
/**
|
||||
* Unregister a new channel (and deallocates its handle).
|
||||
* @param sid SID
|
||||
*/
|
||||
virtual void unregisterChannel(pvAccessID sid) = 0;
|
||||
|
||||
/**
|
||||
* Get channel by its SID.
|
||||
* @param sid channel SID
|
||||
* @return channel with given SID, <code>null</code> otherwise
|
||||
*/
|
||||
virtual ServerChannel::shared_pointer getChannel(pvAccessID sid) = 0;
|
||||
|
||||
/**
|
||||
* Get channel count.
|
||||
* @return channel count.
|
||||
*/
|
||||
virtual int getChannelCount() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A request that expects an response.
|
||||
* Responses identified by its I/O ID.
|
||||
*/
|
||||
class ResponseRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(ResponseRequest);
|
||||
|
||||
virtual ~ResponseRequest() {}
|
||||
|
||||
/**
|
||||
* Get I/O ID.
|
||||
* @return ioid
|
||||
*/
|
||||
virtual pvAccessID getIOID() const = 0;
|
||||
|
||||
/**
|
||||
* Timeout notification.
|
||||
*/
|
||||
virtual void timeout() = 0;
|
||||
|
||||
/**
|
||||
* Cancel response request (always to be called to complete/destroy).
|
||||
*/
|
||||
virtual void cancel() = 0;
|
||||
|
||||
/**
|
||||
* Report status to clients (e.g. disconnected).
|
||||
* @param status to report.
|
||||
*/
|
||||
virtual void reportStatus(epics::pvData::Status const & status) = 0;
|
||||
|
||||
/**
|
||||
* Get request requester.
|
||||
* @return request requester.
|
||||
*/
|
||||
virtual std::tr1::shared_ptr<epics::pvData::Requester> getRequester() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
*/
|
||||
class DataResponse : public ResponseRequest {
|
||||
public:
|
||||
POINTER_DEFINITIONS(DataResponse);
|
||||
|
||||
virtual ~DataResponse() {}
|
||||
|
||||
/**
|
||||
* Notification response.
|
||||
* @param transport
|
||||
* @param version
|
||||
* @param payloadBuffer
|
||||
*/
|
||||
virtual void response(Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::ByteBuffer* payloadBuffer) = 0;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* A request that expects an response multiple responses.
|
||||
* Responses identified by its I/O ID.
|
||||
* This interface needs to be extended (to provide method called on response).
|
||||
*/
|
||||
class SubscriptionRequest /*: public ResponseRequest*/ {
|
||||
public:
|
||||
POINTER_DEFINITIONS(SubscriptionRequest);
|
||||
|
||||
virtual ~SubscriptionRequest() {}
|
||||
|
||||
/**
|
||||
* Update (e.g. after some time of unresponsiveness) - report current value.
|
||||
*/
|
||||
virtual void updateSubscription() = 0;
|
||||
|
||||
/**
|
||||
* Rescubscribe (e.g. when server was restarted)
|
||||
* @param transport new transport to be used.
|
||||
*/
|
||||
virtual void resubscribeSubscription(Transport::shared_pointer const & transport) = 0;
|
||||
};
|
||||
|
||||
|
||||
struct AtomicBoolean_null_deleter
|
||||
{
|
||||
void operator()(void const *) const {}
|
||||
};
|
||||
|
||||
// standard performance on set/clear, use of tr1::shared_ptr lock-free counter for get
|
||||
// alternative is to use boost::atomic
|
||||
class AtomicBoolean
|
||||
{
|
||||
public:
|
||||
AtomicBoolean() : counter(static_cast<void*>(0), AtomicBoolean_null_deleter()) {};
|
||||
|
||||
void set() { mutex.lock(); setp = counter; mutex.unlock(); }
|
||||
void clear() { mutex.lock(); setp.reset(); mutex.unlock(); }
|
||||
|
||||
bool get() const { return counter.use_count() == 2; }
|
||||
private:
|
||||
std::tr1::shared_ptr<void> counter;
|
||||
std::tr1::shared_ptr<void> setp;
|
||||
epics::pvData::Mutex mutex;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* REMOTE_H_ */
|
||||
80
src/remote/serializationHelper.cpp
Normal file
80
src/remote/serializationHelper.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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/serializationHelper.h>
|
||||
#include <pv/introspectionRegistry.h>
|
||||
#include <pv/convert.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
PVDataCreatePtr SerializationHelper::_pvDataCreate(getPVDataCreate());
|
||||
|
||||
PVStructure::shared_pointer SerializationHelper::deserializePVRequest(ByteBuffer* buffer, DeserializableControl* control)
|
||||
{
|
||||
// for now ordinary structure, later can be changed
|
||||
return deserializeStructureFull(buffer, control);
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer SerializationHelper::deserializeStructureAndCreatePVStructure(ByteBuffer* buffer, DeserializableControl* control)
|
||||
{
|
||||
return deserializeStructureAndCreatePVStructure(buffer, control, PVStructure::shared_pointer());
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer SerializationHelper::deserializeStructureAndCreatePVStructure(ByteBuffer* buffer, DeserializableControl* control, PVStructure::shared_pointer const & existingStructure)
|
||||
{
|
||||
FieldConstPtr field = control->cachedDeserialize(buffer);
|
||||
if (field.get() == 0)
|
||||
return PVStructure::shared_pointer();
|
||||
|
||||
if (existingStructure.get() != 0 && *(field.get()) == *(existingStructure->getField()))
|
||||
return existingStructure;
|
||||
else
|
||||
return _pvDataCreate->createPVStructure(std::tr1::static_pointer_cast<const Structure>(field));
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer SerializationHelper::deserializeStructureFull(ByteBuffer* buffer, DeserializableControl* control)
|
||||
{
|
||||
PVStructure::shared_pointer pvStructure;
|
||||
FieldConstPtr structureField = control->cachedDeserialize(buffer);
|
||||
if (structureField.get() != 0)
|
||||
{
|
||||
pvStructure = _pvDataCreate->createPVStructure(std::tr1::static_pointer_cast<const Structure>(structureField));
|
||||
pvStructure->deserialize(buffer, control);
|
||||
}
|
||||
return pvStructure;
|
||||
}
|
||||
|
||||
void SerializationHelper::serializeNullField(ByteBuffer* buffer, SerializableControl* control)
|
||||
{
|
||||
control->ensureBuffer(1);
|
||||
buffer->putByte(IntrospectionRegistry::NULL_TYPE_CODE);
|
||||
}
|
||||
|
||||
void SerializationHelper::serializePVRequest(ByteBuffer* buffer, SerializableControl* control, PVStructure::shared_pointer const & pvRequest)
|
||||
{
|
||||
// for now ordinary structure, later can be changed
|
||||
serializeStructureFull(buffer, control, pvRequest);
|
||||
}
|
||||
|
||||
void SerializationHelper::serializeStructureFull(ByteBuffer* buffer, SerializableControl* control, PVStructure::shared_pointer const & pvStructure)
|
||||
{
|
||||
if (pvStructure.get() == 0)
|
||||
{
|
||||
serializeNullField(buffer, control);
|
||||
}
|
||||
else
|
||||
{
|
||||
control->cachedSerialize(pvStructure->getField(), buffer);
|
||||
pvStructure->serialize(buffer, control);
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
|
||||
89
src/remote/serializationHelper.h
Normal file
89
src/remote/serializationHelper.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* serializationHelper.h
|
||||
*
|
||||
* Created on: Jul 24, 2012
|
||||
* Author: msekoranja
|
||||
*/
|
||||
|
||||
#ifndef SERIALIZATIONHELPER_H_
|
||||
#define SERIALIZATIONHELPER_H_
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define serializationHelperEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/serialize.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/noDefaultMethods.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
|
||||
#ifdef serializationHelperEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef serializationHelperEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvaConstants.h>
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class SerializationHelper : public epics::pvData::NoDefaultMethods {
|
||||
public:
|
||||
|
||||
static epics::pvData::PVDataCreatePtr _pvDataCreate;
|
||||
|
||||
/**
|
||||
* Deserialize PVRequest.
|
||||
* @param payloadBuffer data buffer.
|
||||
* @return deserialized PVRequest, can be <code>null</code>.
|
||||
*/
|
||||
static epics::pvData::PVStructure::shared_pointer deserializePVRequest(epics::pvData::ByteBuffer* payloadBuffer, epics::pvData::DeserializableControl* control);
|
||||
|
||||
/**
|
||||
* Deserialize Structure and create PVStructure instance.
|
||||
* @param payloadBuffer data buffer.
|
||||
* @param control deserialization control.
|
||||
* @return PVStructure instance, can be <code>null</code>.
|
||||
*/
|
||||
static epics::pvData::PVStructure::shared_pointer deserializeStructureAndCreatePVStructure(epics::pvData::ByteBuffer* payloadBuffer, epics::pvData::DeserializableControl* control);
|
||||
|
||||
/**
|
||||
* Deserialize Structure and create PVStructure instance, if necessary.
|
||||
* @param payloadBuffer data buffer.
|
||||
* @param control deserialization control.
|
||||
* @param existingStructure if deserialized Field matches <code>existingStrcuture</code> Field, then
|
||||
* <code>existingStructure</code> instance is returned. <code>null</code> value is allowed.
|
||||
* @return PVStructure instance, can be <code>null</code>.
|
||||
*/
|
||||
static epics::pvData::PVStructure::shared_pointer deserializeStructureAndCreatePVStructure(epics::pvData::ByteBuffer* payloadBuffer, epics::pvData::DeserializableControl* control, epics::pvData::PVStructure::shared_pointer const & existingStructure);
|
||||
|
||||
/**
|
||||
* Deserialize optional PVStructrue.
|
||||
* @param payloadBuffer data buffer.
|
||||
* @return deserialized PVStructure, can be <code>null</code>.
|
||||
*/
|
||||
static epics::pvData::PVStructure::shared_pointer deserializeStructureFull(epics::pvData::ByteBuffer* payloadBuffer, epics::pvData::DeserializableControl* control);
|
||||
|
||||
static void serializeNullField(epics::pvData::ByteBuffer* buffer, epics::pvData::SerializableControl* control);
|
||||
|
||||
/**
|
||||
* Serialize PVRequest.
|
||||
* @param buffer data buffer.
|
||||
*/
|
||||
static void serializePVRequest(epics::pvData::ByteBuffer* buffer, epics::pvData::SerializableControl* control, epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
/**
|
||||
* Serialize optional PVStructrue.
|
||||
* @param buffer data buffer.
|
||||
*/
|
||||
static void serializeStructureFull(epics::pvData::ByteBuffer* buffer, epics::pvData::SerializableControl* control, epics::pvData::PVStructure::shared_pointer const & pvStructure);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SERIALIZATIONHELPER_H_ */
|
||||
333
src/remote/simpleChannelSearchManagerImpl.cpp
Normal file
333
src/remote/simpleChannelSearchManagerImpl.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
/**
|
||||
* 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/simpleChannelSearchManagerImpl.h>
|
||||
#include <pv/pvaConstants.h>
|
||||
#include <pv/blockingUDP.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
const int SimpleChannelSearchManagerImpl::DATA_COUNT_POSITION = PVA_MESSAGE_HEADER_SIZE + sizeof(int32)/sizeof(int8) + 1;
|
||||
const int SimpleChannelSearchManagerImpl::PAYLOAD_POSITION = sizeof(int16)/sizeof(int8) + 2;
|
||||
|
||||
// 225ms +/- 25ms random
|
||||
const double SimpleChannelSearchManagerImpl::ATOMIC_PERIOD = 0.225;
|
||||
const int SimpleChannelSearchManagerImpl::PERIOD_JITTER_MS = 25;
|
||||
|
||||
const int SimpleChannelSearchManagerImpl::BOOST_VALUE = 1;
|
||||
// must be power of two (so that search is done)
|
||||
const int SimpleChannelSearchManagerImpl::MAX_COUNT_VALUE = 1 << 7;
|
||||
const int SimpleChannelSearchManagerImpl::MAX_FALLBACK_COUNT_VALUE = (1 << 6) + 1;
|
||||
|
||||
const int SimpleChannelSearchManagerImpl::MAX_FRAMES_AT_ONCE = 10;
|
||||
const int SimpleChannelSearchManagerImpl::DELAY_BETWEEN_FRAMES_MS = 50;
|
||||
|
||||
|
||||
SimpleChannelSearchManagerImpl::shared_pointer
|
||||
SimpleChannelSearchManagerImpl::create(Context::shared_pointer const & context)
|
||||
{
|
||||
SimpleChannelSearchManagerImpl::shared_pointer thisPtr(new SimpleChannelSearchManagerImpl(context));
|
||||
thisPtr->activate();
|
||||
return thisPtr;
|
||||
}
|
||||
|
||||
SimpleChannelSearchManagerImpl::SimpleChannelSearchManagerImpl(Context::shared_pointer const & context) :
|
||||
m_context(context),
|
||||
m_canceled(),
|
||||
m_sequenceNumber(0),
|
||||
m_sendBuffer(MAX_UDP_UNFRAGMENTED_SEND),
|
||||
m_channels(),
|
||||
m_lastTimeSent(),
|
||||
m_mockTransportSendControl(),
|
||||
m_channelMutex(),
|
||||
m_userValueMutex(),
|
||||
m_mutex()
|
||||
{
|
||||
// initialize send buffer
|
||||
initializeSendBuffer();
|
||||
|
||||
|
||||
// initialize random seed with some random value
|
||||
srand ( time(NULL) );
|
||||
}
|
||||
|
||||
void SimpleChannelSearchManagerImpl::activate()
|
||||
{
|
||||
// add some jitter so that all the clients do not send at the same time
|
||||
double period = ATOMIC_PERIOD + (rand() % (2*PERIOD_JITTER_MS+1) - PERIOD_JITTER_MS)/(double)1000;
|
||||
|
||||
Context::shared_pointer context = m_context.lock();
|
||||
if (context.get())
|
||||
context->getTimer()->schedulePeriodic(shared_from_this(), period, period);
|
||||
|
||||
//new Thread(this, "pvAccess immediate-search").start();
|
||||
}
|
||||
|
||||
SimpleChannelSearchManagerImpl::~SimpleChannelSearchManagerImpl()
|
||||
{
|
||||
// shared_from_this() is not allowed from destructor
|
||||
// be sure to call cancel() first
|
||||
// cancel();
|
||||
}
|
||||
|
||||
void SimpleChannelSearchManagerImpl::cancel()
|
||||
{
|
||||
Lock guard(m_mutex);
|
||||
|
||||
if (m_canceled.get())
|
||||
return;
|
||||
m_canceled.set();
|
||||
|
||||
Context::shared_pointer context = m_context.lock();
|
||||
if (context.get())
|
||||
context->getTimer()->cancel(shared_from_this());
|
||||
}
|
||||
|
||||
int32_t SimpleChannelSearchManagerImpl::registeredCount()
|
||||
{
|
||||
Lock guard(m_channelMutex);
|
||||
return static_cast<int32_t>(m_channels.size());
|
||||
}
|
||||
|
||||
void SimpleChannelSearchManagerImpl::registerSearchInstance(SearchInstance::shared_pointer const & channel)
|
||||
{
|
||||
if (m_canceled.get())
|
||||
return;
|
||||
|
||||
bool immediateTrigger;
|
||||
{
|
||||
Lock guard(m_channelMutex);
|
||||
//overrides if already registered
|
||||
m_channels[channel->getSearchInstanceID()] = channel;
|
||||
immediateTrigger = m_channels.size() == 1;
|
||||
|
||||
Lock guard2(m_userValueMutex);
|
||||
int32_t& userValue = channel->getUserValue();
|
||||
userValue = 1;
|
||||
}
|
||||
|
||||
if (immediateTrigger)
|
||||
callback();
|
||||
}
|
||||
|
||||
void SimpleChannelSearchManagerImpl::unregisterSearchInstance(SearchInstance::shared_pointer const & channel)
|
||||
{
|
||||
Lock guard(m_channelMutex);
|
||||
pvAccessID id = channel->getSearchInstanceID();
|
||||
std::map<pvAccessID,SearchInstance::shared_pointer>::iterator channelsIter = m_channels.find(id);
|
||||
if(channelsIter != m_channels.end())
|
||||
m_channels.erase(id);
|
||||
}
|
||||
|
||||
void SimpleChannelSearchManagerImpl::searchResponse(pvAccessID cid, int32_t /*seqNo*/, int8_t minorRevision, osiSockAddr* serverAddress)
|
||||
{
|
||||
Lock guard(m_channelMutex);
|
||||
std::map<pvAccessID,SearchInstance::shared_pointer>::iterator channelsIter = m_channels.find(cid);
|
||||
if(channelsIter == m_channels.end())
|
||||
{
|
||||
guard.unlock();
|
||||
|
||||
// minor hack to enable duplicate reports
|
||||
SearchInstance::shared_pointer si = std::tr1::dynamic_pointer_cast<SearchInstance>(m_context.lock()->getChannel(cid));
|
||||
if (si)
|
||||
si->searchResponse(minorRevision, serverAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
SearchInstance::shared_pointer si = channelsIter->second;
|
||||
|
||||
// remove from search list
|
||||
m_channels.erase(cid);
|
||||
|
||||
guard.unlock();
|
||||
|
||||
// then notify SearchInstance
|
||||
si->searchResponse(minorRevision, serverAddress);
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleChannelSearchManagerImpl::newServerDetected()
|
||||
{
|
||||
boost();
|
||||
callback();
|
||||
}
|
||||
|
||||
void SimpleChannelSearchManagerImpl::initializeSendBuffer()
|
||||
{
|
||||
// for now OK, since it is only set here
|
||||
m_sequenceNumber++;
|
||||
|
||||
|
||||
// new buffer
|
||||
m_sendBuffer.clear();
|
||||
m_sendBuffer.putByte(PVA_MAGIC);
|
||||
m_sendBuffer.putByte(PVA_VERSION);
|
||||
m_sendBuffer.putByte((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) ? 0x80 : 0x00); // data + 7-bit endianess
|
||||
m_sendBuffer.putByte((int8_t)3); // search
|
||||
m_sendBuffer.putInt(sizeof(int32_t)/sizeof(int8_t) + 1); // "zero" payload
|
||||
m_sendBuffer.putInt(m_sequenceNumber);
|
||||
|
||||
/*
|
||||
final boolean REQUIRE_REPLY = false;
|
||||
sendBuffer.put(REQUIRE_REPLY ? (byte)QoS.REPLY_REQUIRED.getMaskValue() : (byte)QoS.DEFAULT.getMaskValue());
|
||||
*/
|
||||
|
||||
m_sendBuffer.putByte((int8_t)QOS_DEFAULT);
|
||||
m_sendBuffer.putShort((int16_t)0); // count
|
||||
}
|
||||
|
||||
void SimpleChannelSearchManagerImpl::flushSendBuffer()
|
||||
{
|
||||
Lock guard(m_mutex);
|
||||
|
||||
Transport::shared_pointer tt = m_context.lock()->getSearchTransport();
|
||||
BlockingUDPTransport::shared_pointer ut = std::tr1::static_pointer_cast<BlockingUDPTransport>(tt);
|
||||
ut->send(&m_sendBuffer); // TODO
|
||||
initializeSendBuffer();
|
||||
}
|
||||
|
||||
|
||||
bool SimpleChannelSearchManagerImpl::generateSearchRequestMessage(SearchInstance::shared_pointer const & channel,
|
||||
ByteBuffer* requestMessage, TransportSendControl* control)
|
||||
{
|
||||
epics::pvData::int16 dataCount = requestMessage->getShort(DATA_COUNT_POSITION);
|
||||
|
||||
dataCount++;
|
||||
|
||||
/*
|
||||
if(dataCount >= MAX_SEARCH_BATCH_COUNT)
|
||||
return false;
|
||||
*/
|
||||
|
||||
const epics::pvData::String name = channel->getSearchInstanceName();
|
||||
// not nice...
|
||||
const int addedPayloadSize = sizeof(int32)/sizeof(int8) + (1 + sizeof(int32)/sizeof(int8) + name.length());
|
||||
if(((int)requestMessage->getRemaining()) < addedPayloadSize)
|
||||
return false;
|
||||
|
||||
requestMessage->putInt(channel->getSearchInstanceID());
|
||||
SerializeHelper::serializeString(name, requestMessage, control);
|
||||
|
||||
requestMessage->putInt(PAYLOAD_POSITION, requestMessage->getPosition() - PVA_MESSAGE_HEADER_SIZE);
|
||||
requestMessage->putShort(DATA_COUNT_POSITION, dataCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SimpleChannelSearchManagerImpl::generateSearchRequestMessage(SearchInstance::shared_pointer const & channel,
|
||||
bool allowNewFrame, bool flush)
|
||||
{
|
||||
Lock guard(m_mutex);
|
||||
bool success = generateSearchRequestMessage(channel, &m_sendBuffer, &m_mockTransportSendControl);
|
||||
// buffer full, flush
|
||||
if(!success)
|
||||
{
|
||||
flushSendBuffer();
|
||||
if(allowNewFrame)
|
||||
generateSearchRequestMessage(channel, &m_sendBuffer, &m_mockTransportSendControl);
|
||||
if (flush)
|
||||
flushSendBuffer();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flush)
|
||||
flushSendBuffer();
|
||||
|
||||
return flush;
|
||||
}
|
||||
|
||||
void SimpleChannelSearchManagerImpl::boost()
|
||||
{
|
||||
Lock guard(m_channelMutex);
|
||||
Lock guard2(m_userValueMutex);
|
||||
std::map<pvAccessID,SearchInstance::shared_pointer>::iterator channelsIter = m_channels.begin();
|
||||
for(; channelsIter != m_channels.end(); channelsIter++)
|
||||
{
|
||||
int32_t& userValue = channelsIter->second->getUserValue();
|
||||
userValue = BOOST_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleChannelSearchManagerImpl::callback()
|
||||
{
|
||||
// high-frequency beacon anomaly trigger guard
|
||||
{
|
||||
Lock guard(m_mutex);
|
||||
|
||||
epics::pvData::TimeStamp now;
|
||||
now.getCurrent();
|
||||
int64_t nowMS = now.getMilliseconds();
|
||||
|
||||
if (nowMS - m_lastTimeSent < 100)
|
||||
return;
|
||||
m_lastTimeSent = nowMS;
|
||||
}
|
||||
|
||||
|
||||
int count = 0;
|
||||
int frameSent = 0;
|
||||
|
||||
vector<SearchInstance::shared_pointer> toSend;
|
||||
{
|
||||
Lock guard(m_channelMutex);
|
||||
toSend.reserve(m_channels.size());
|
||||
std::map<pvAccessID,SearchInstance::shared_pointer>::iterator channelsIter = m_channels.begin();
|
||||
for(; channelsIter != m_channels.end(); channelsIter++)
|
||||
toSend.push_back(channelsIter->second);
|
||||
}
|
||||
|
||||
vector<SearchInstance::shared_pointer>::iterator siter = toSend.begin();
|
||||
for (; siter != toSend.end(); siter++)
|
||||
{
|
||||
m_userValueMutex.lock();
|
||||
int32_t& countValue = (*siter)->getUserValue();
|
||||
bool skip = !isPowerOfTwo(countValue);
|
||||
|
||||
if (countValue == MAX_COUNT_VALUE)
|
||||
countValue = MAX_FALLBACK_COUNT_VALUE;
|
||||
else
|
||||
countValue++;
|
||||
m_userValueMutex.unlock();
|
||||
|
||||
// back-off
|
||||
if (skip)
|
||||
continue;
|
||||
|
||||
count++;
|
||||
|
||||
if (generateSearchRequestMessage(*siter, true, false))
|
||||
frameSent++;
|
||||
if (frameSent == MAX_FRAMES_AT_ONCE)
|
||||
{
|
||||
epicsThreadSleep(DELAY_BETWEEN_FRAMES_MS/(double)1000.0);
|
||||
frameSent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
flushSendBuffer();
|
||||
}
|
||||
|
||||
bool SimpleChannelSearchManagerImpl::isPowerOfTwo(int32_t x)
|
||||
{
|
||||
return ((x > 0) && (x & (x - 1)) == 0);
|
||||
}
|
||||
|
||||
void SimpleChannelSearchManagerImpl::timerStopped()
|
||||
{
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
199
src/remote/simpleChannelSearchManagerImpl.h
Normal file
199
src/remote/simpleChannelSearchManagerImpl.h
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SIMPLECHANNELSEARCHMANAGERIMPL_H
|
||||
#define SIMPLECHANNELSEARCHMANAGERIMPL_H
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define simpleChannelSearchManagerEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/timer.h>
|
||||
|
||||
#ifdef simpleChannelSearchManagerEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef simpleChannelSearchManagerEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/channelSearchManager.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
|
||||
class MockTransportSendControl: public TransportSendControl
|
||||
{
|
||||
public:
|
||||
void endMessage() {}
|
||||
void flush(bool /*lastMessageCompleted*/) {}
|
||||
void setRecipient(const osiSockAddr& /*sendTo*/) {}
|
||||
void startMessage(epics::pvData::int8 /*command*/, std::size_t /*ensureCapacity*/) {}
|
||||
void ensureBuffer(std::size_t /*size*/) {}
|
||||
void alignBuffer(std::size_t /*alignment*/) {}
|
||||
void flushSerializeBuffer() {}
|
||||
void cachedSerialize(const std::tr1::shared_ptr<const epics::pvData::Field>& field, epics::pvData::ByteBuffer* buffer)
|
||||
{
|
||||
// no cache
|
||||
field->serialize(buffer, this);
|
||||
}
|
||||
virtual bool directSerialize(epics::pvData::ByteBuffer* /*existingBuffer*/, const char* /*toSerialize*/,
|
||||
std::size_t /*elementCount*/, std::size_t /*elementSize*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SimpleChannelSearchManagerImpl :
|
||||
public ChannelSearchManager,
|
||||
public epics::pvData::TimerCallback,
|
||||
public std::tr1::enable_shared_from_this<SimpleChannelSearchManagerImpl>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(SimpleChannelSearchManagerImpl);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param context
|
||||
*/
|
||||
static shared_pointer create(Context::shared_pointer const & context);
|
||||
/**
|
||||
* Constructor.
|
||||
* @param context
|
||||
*/
|
||||
virtual ~SimpleChannelSearchManagerImpl();
|
||||
/**
|
||||
* Cancel.
|
||||
*/
|
||||
void cancel();
|
||||
/**
|
||||
* Get number of registered channels.
|
||||
* @return number of registered channels.
|
||||
*/
|
||||
int32_t registeredCount();
|
||||
/**
|
||||
* Register channel.
|
||||
* @param channel to register.
|
||||
*/
|
||||
void registerSearchInstance(SearchInstance::shared_pointer const & channel);
|
||||
/**
|
||||
* Unregister channel.
|
||||
* @param channel to unregister.
|
||||
*/
|
||||
void unregisterSearchInstance(SearchInstance::shared_pointer const & channel);
|
||||
/**
|
||||
* Search response from server (channel found).
|
||||
* @param cid client channel ID.
|
||||
* @param seqNo search sequence number.
|
||||
* @param minorRevision server minor PVA revision.
|
||||
* @param serverAddress server address.
|
||||
*/
|
||||
void searchResponse(pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress);
|
||||
/**
|
||||
* New server detected.
|
||||
* Boost searching of all channels.
|
||||
*/
|
||||
void newServerDetected();
|
||||
|
||||
/// Timer callback.
|
||||
void callback();
|
||||
|
||||
/// Timer stooped callback.
|
||||
void timerStopped();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
* @param context
|
||||
*/
|
||||
SimpleChannelSearchManagerImpl(Context::shared_pointer const & context);
|
||||
void activate();
|
||||
|
||||
bool generateSearchRequestMessage(SearchInstance::shared_pointer const & channel, bool allowNewFrame, bool flush);
|
||||
|
||||
static bool generateSearchRequestMessage(SearchInstance::shared_pointer const & channel,
|
||||
epics::pvData::ByteBuffer* byteBuffer, TransportSendControl* control);
|
||||
|
||||
void boost();
|
||||
|
||||
void initializeSendBuffer();
|
||||
void flushSendBuffer();
|
||||
|
||||
static bool isPowerOfTwo(int32_t x);
|
||||
|
||||
/**
|
||||
* Context.
|
||||
*/
|
||||
Context::weak_pointer m_context;
|
||||
|
||||
/**
|
||||
* Canceled flag.
|
||||
*/
|
||||
AtomicBoolean m_canceled;
|
||||
|
||||
/**
|
||||
* Search (datagram) sequence number.
|
||||
*/
|
||||
int32_t m_sequenceNumber;
|
||||
|
||||
/**
|
||||
* Send byte buffer (frame)
|
||||
*/
|
||||
epics::pvData::ByteBuffer m_sendBuffer;
|
||||
|
||||
/**
|
||||
* Set of registered channels.
|
||||
*/
|
||||
std::map<pvAccessID,SearchInstance::shared_pointer> m_channels;
|
||||
|
||||
/**
|
||||
* Time of last frame send.
|
||||
*/
|
||||
int64_t m_lastTimeSent;
|
||||
|
||||
/**
|
||||
* Mock transport send control
|
||||
*/
|
||||
MockTransportSendControl m_mockTransportSendControl;
|
||||
|
||||
/**
|
||||
* This instance mutex.
|
||||
*/
|
||||
epics::pvData::Mutex m_channelMutex;
|
||||
|
||||
/**
|
||||
* User value lock.
|
||||
*/
|
||||
epics::pvData::Mutex m_userValueMutex;
|
||||
|
||||
/**
|
||||
* m_channels mutex.
|
||||
*/
|
||||
epics::pvData::Mutex m_mutex;
|
||||
|
||||
static const int DATA_COUNT_POSITION;
|
||||
static const int PAYLOAD_POSITION;
|
||||
|
||||
static const double ATOMIC_PERIOD;
|
||||
static const int PERIOD_JITTER_MS;
|
||||
|
||||
static const int BOOST_VALUE;
|
||||
static const int MAX_COUNT_VALUE;
|
||||
static const int MAX_FALLBACK_COUNT_VALUE;
|
||||
|
||||
static const int MAX_FRAMES_AT_ONCE;
|
||||
static const int DELAY_BETWEEN_FRAMES_MS;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SIMPLECHANNELSEARCHMANAGERIMPL_H */
|
||||
181
src/remote/transportRegistry.cpp
Normal file
181
src/remote/transportRegistry.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* 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/transportRegistry.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
TransportRegistry::TransportRegistry(): _transports(), _transportCount(0), _mutex()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TransportRegistry::~TransportRegistry()
|
||||
{
|
||||
}
|
||||
|
||||
void TransportRegistry::put(Transport::shared_pointer const & transport)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
//const String type = transport.getType();
|
||||
const int16 priority = transport->getPriority();
|
||||
const osiSockAddr* address = transport->getRemoteAddress();
|
||||
|
||||
transportsMap_t::iterator transportsIter = _transports.find(address);
|
||||
prioritiesMapSharedPtr_t priorities;
|
||||
if(transportsIter == _transports.end())
|
||||
{
|
||||
priorities.reset(new prioritiesMap_t());
|
||||
_transports[address] = priorities;
|
||||
_transportCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
priorities = transportsIter->second;
|
||||
prioritiesMap_t::iterator prioritiesIter = priorities->find(priority);
|
||||
if(prioritiesIter == priorities->end()) //only increase transportCount if not replacing
|
||||
{
|
||||
_transportCount++;
|
||||
}
|
||||
}
|
||||
(*priorities)[priority] = transport;
|
||||
}
|
||||
|
||||
Transport::shared_pointer TransportRegistry::get(String const & /*type*/, const osiSockAddr* address, const int16 priority)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
transportsMap_t::iterator transportsIter = _transports.find(address);
|
||||
if(transportsIter != _transports.end())
|
||||
{
|
||||
prioritiesMapSharedPtr_t priorities = transportsIter->second;
|
||||
prioritiesMap_t::iterator prioritiesIter = priorities->find(priority);
|
||||
if(prioritiesIter != priorities->end())
|
||||
{
|
||||
return prioritiesIter->second;
|
||||
}
|
||||
}
|
||||
return Transport::shared_pointer();
|
||||
}
|
||||
|
||||
auto_ptr<TransportRegistry::transportVector_t> TransportRegistry::get(String const & /*type*/, const osiSockAddr* address)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
transportsMap_t::iterator transportsIter = _transports.find(address);
|
||||
if(transportsIter != _transports.end())
|
||||
{
|
||||
prioritiesMapSharedPtr_t priorities = transportsIter->second;
|
||||
auto_ptr<transportVector_t> transportArray(new transportVector_t(priorities->size()));
|
||||
int32 i = 0;
|
||||
for(prioritiesMap_t::iterator prioritiesIter = priorities->begin();
|
||||
prioritiesIter != priorities->end();
|
||||
prioritiesIter++, i++)
|
||||
{
|
||||
(*transportArray)[i] = prioritiesIter->second;
|
||||
}
|
||||
return transportArray;
|
||||
}
|
||||
return auto_ptr<transportVector_t>();
|
||||
}
|
||||
|
||||
Transport::shared_pointer TransportRegistry::remove(Transport::shared_pointer const & transport)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
const int16 priority = transport->getPriority();
|
||||
const osiSockAddr* address = transport->getRemoteAddress();
|
||||
Transport::shared_pointer retTransport;
|
||||
transportsMap_t::iterator transportsIter = _transports.find(address);
|
||||
if(transportsIter != _transports.end())
|
||||
{
|
||||
prioritiesMapSharedPtr_t priorities = transportsIter->second;
|
||||
prioritiesMap_t::iterator prioritiesIter = priorities->find(priority);
|
||||
if(prioritiesIter != priorities->end())
|
||||
{
|
||||
retTransport = prioritiesIter->second;
|
||||
priorities->erase(prioritiesIter);
|
||||
_transportCount--;
|
||||
if(priorities->size() == 0)
|
||||
{
|
||||
_transports.erase(transportsIter);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retTransport;
|
||||
}
|
||||
|
||||
void TransportRegistry::clear()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
_transports.clear();
|
||||
_transportCount = 0;
|
||||
}
|
||||
|
||||
int32 TransportRegistry::numberOfActiveTransports()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
return _transportCount;
|
||||
}
|
||||
|
||||
|
||||
auto_ptr<TransportRegistry::transportVector_t> TransportRegistry::toArray(String const & /*type*/)
|
||||
{
|
||||
// TODO support type
|
||||
return toArray();
|
||||
}
|
||||
|
||||
|
||||
auto_ptr<TransportRegistry::transportVector_t> TransportRegistry::toArray()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
if (_transportCount == 0)
|
||||
return auto_ptr<transportVector_t>(0);
|
||||
|
||||
auto_ptr<transportVector_t> transportArray(new transportVector_t(_transportCount));
|
||||
|
||||
int32 i = 0;
|
||||
for (transportsMap_t::iterator transportsIter = _transports.begin();
|
||||
transportsIter != _transports.end();
|
||||
transportsIter++)
|
||||
{
|
||||
prioritiesMapSharedPtr_t priorities = transportsIter->second;
|
||||
for (prioritiesMap_t::iterator prioritiesIter = priorities->begin();
|
||||
prioritiesIter != priorities->end();
|
||||
prioritiesIter++, i++)
|
||||
{
|
||||
(*transportArray)[i] = prioritiesIter->second;
|
||||
}
|
||||
}
|
||||
|
||||
return transportArray;
|
||||
}
|
||||
|
||||
void TransportRegistry::toArray(transportVector_t & transportArray)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
if (_transportCount == 0)
|
||||
return;
|
||||
|
||||
transportArray.reserve(transportArray.size() + _transportCount);
|
||||
|
||||
for (transportsMap_t::iterator transportsIter = _transports.begin();
|
||||
transportsIter != _transports.end();
|
||||
transportsIter++)
|
||||
{
|
||||
prioritiesMapSharedPtr_t priorities = transportsIter->second;
|
||||
for (prioritiesMap_t::iterator prioritiesIter = priorities->begin();
|
||||
prioritiesIter != priorities->end();
|
||||
prioritiesIter++)
|
||||
{
|
||||
transportArray.push_back(prioritiesIter->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
73
src/remote/transportRegistry.h
Normal file
73
src/remote/transportRegistry.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TRANSPORTREGISTRY_H
|
||||
#define TRANSPORTREGISTRY_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define transportRegistryEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <osiSock.h>
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/remote.h>
|
||||
#include <pv/inetAddressUtil.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#ifdef transportRegistryEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef transportRegistryEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class TransportRegistry {
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<TransportRegistry> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const TransportRegistry> const_shared_pointer;
|
||||
|
||||
typedef std::vector<Transport::shared_pointer> transportVector_t;
|
||||
|
||||
TransportRegistry();
|
||||
virtual ~TransportRegistry();
|
||||
|
||||
void put(Transport::shared_pointer const & transport);
|
||||
Transport::shared_pointer get(epics::pvData::String const & type, const osiSockAddr* address, const epics::pvData::int16 priority);
|
||||
std::auto_ptr<transportVector_t> get(epics::pvData::String const & type, const osiSockAddr* address);
|
||||
Transport::shared_pointer remove(Transport::shared_pointer const & transport);
|
||||
void clear();
|
||||
epics::pvData::int32 numberOfActiveTransports();
|
||||
|
||||
// TODO note type not supported
|
||||
std::auto_ptr<transportVector_t> toArray(epics::pvData::String const & type);
|
||||
std::auto_ptr<transportVector_t> toArray();
|
||||
// optimized to avoid reallocation, adds to array
|
||||
void toArray(transportVector_t & transportArray);
|
||||
|
||||
private:
|
||||
//TODO if unordered map is used instead of map we can use sockAddrAreIdentical routine from osiSock.h
|
||||
// NOTE: pointers are used to osiSockAddr (to save memory), since it guaranteed that their reference is valid as long as Transport
|
||||
typedef std::map<const epics::pvData::int16,Transport::shared_pointer> prioritiesMap_t;
|
||||
typedef std::tr1::shared_ptr<prioritiesMap_t> prioritiesMapSharedPtr_t;
|
||||
typedef std::map<const osiSockAddr*,prioritiesMapSharedPtr_t,comp_osiSockAddrPtr> transportsMap_t;
|
||||
|
||||
transportsMap_t _transports;
|
||||
epics::pvData::int32 _transportCount;
|
||||
epics::pvData::Mutex _mutex;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* INTROSPECTIONREGISTRY_H */
|
||||
7
src/remoteClient/Makefile
Normal file
7
src/remoteClient/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/remoteClient
|
||||
|
||||
INC += clientContextImpl.h
|
||||
|
||||
LIBSRCS += clientContextImpl.cpp
|
||||
4400
src/remoteClient/clientContextImpl.cpp
Normal file
4400
src/remoteClient/clientContextImpl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
136
src/remoteClient/clientContextImpl.h
Normal file
136
src/remoteClient/clientContextImpl.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CLIENTCONTEXTIMPL_H_
|
||||
#define CLIENTCONTEXTIMPL_H_
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define clientContextImplEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#ifdef clientContextImplEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef clientContextImplEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/remote.h>
|
||||
#include <pv/channelSearchManager.h>
|
||||
#include <pv/inetAddressUtil.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
class ChannelSearchManager;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class BeaconHandler;
|
||||
class ClientContextImpl;
|
||||
|
||||
class ChannelImpl :
|
||||
public Channel,
|
||||
public TransportClient,
|
||||
public TransportSender,
|
||||
public SearchInstance
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ChannelImpl);
|
||||
|
||||
virtual pvAccessID getChannelID() = 0;
|
||||
virtual void destroyChannel(bool force) = 0;
|
||||
virtual void connectionCompleted(pvAccessID sid/*, rights*/) = 0;
|
||||
virtual void createChannelFailed() = 0;
|
||||
virtual std::tr1::shared_ptr<ClientContextImpl> getContext() = 0;
|
||||
|
||||
virtual pvAccessID getServerChannelID() = 0;
|
||||
virtual void registerResponseRequest(ResponseRequest::shared_pointer const & responseRequest) = 0;
|
||||
virtual void unregisterResponseRequest(pvAccessID ioid) = 0;
|
||||
virtual Transport::shared_pointer checkAndGetTransport() = 0;
|
||||
virtual Transport::shared_pointer checkDestroyedAndGetTransport() = 0;
|
||||
virtual Transport::shared_pointer getTransport() = 0;
|
||||
|
||||
static epics::pvData::Status channelDestroyed;
|
||||
static epics::pvData::Status channelDisconnected;
|
||||
|
||||
};
|
||||
|
||||
class ClientContextImpl : public Context
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ClientContextImpl);
|
||||
|
||||
static epics::pvData::String PROVIDER_NAME;
|
||||
|
||||
/**
|
||||
* Get context implementation version.
|
||||
* @return version of the context implementation.
|
||||
*/
|
||||
virtual const Version& getVersion() = 0;
|
||||
|
||||
/**
|
||||
* Initialize client context. This method is called immediately after instance construction (call of constructor).
|
||||
*/
|
||||
virtual void initialize() = 0;
|
||||
|
||||
/**
|
||||
* Get channel provider implementation.
|
||||
* @return the channel provider.
|
||||
*/
|
||||
virtual ChannelProvider::shared_pointer const & getProvider() = 0;
|
||||
|
||||
/**
|
||||
* Prints detailed information about the context to the standard output stream.
|
||||
*/
|
||||
virtual void printInfo() = 0;
|
||||
|
||||
/**
|
||||
* Prints detailed information about the context to the specified output stream.
|
||||
* @param out the output stream.
|
||||
*/
|
||||
virtual void printInfo(epics::pvData::StringBuilder out) = 0;
|
||||
|
||||
/**
|
||||
* Dispose (destroy) server context.
|
||||
* This calls <code>destroy()</code> and silently handles all exceptions.
|
||||
*/
|
||||
virtual void dispose() = 0;
|
||||
|
||||
|
||||
virtual ChannelSearchManager::shared_pointer getChannelSearchManager() = 0;
|
||||
virtual void checkChannelName(epics::pvData::String const & name) = 0;
|
||||
|
||||
virtual void registerChannel(ChannelImpl::shared_pointer const & channel) = 0;
|
||||
virtual void unregisterChannel(ChannelImpl::shared_pointer const & channel) = 0;
|
||||
|
||||
virtual void destroyChannel(ChannelImpl::shared_pointer const & channel, bool force) = 0;
|
||||
virtual ChannelImpl::shared_pointer createChannelInternal(epics::pvData::String const &name, ChannelRequester::shared_pointer const & requester, short priority, std::auto_ptr<InetAddrVector>& addresses) = 0;
|
||||
|
||||
virtual ResponseRequest::shared_pointer getResponseRequest(pvAccessID ioid) = 0;
|
||||
virtual pvAccessID registerResponseRequest(ResponseRequest::shared_pointer const & request) = 0;
|
||||
virtual ResponseRequest::shared_pointer unregisterResponseRequest(pvAccessID ioid) = 0;
|
||||
|
||||
|
||||
virtual Transport::shared_pointer getTransport(TransportClient::shared_pointer const & client, osiSockAddr* serverAddress, epics::pvData::int8 minorRevision, epics::pvData::int16 priority) = 0;
|
||||
|
||||
virtual void newServerDetected() = 0;
|
||||
|
||||
virtual std::tr1::shared_ptr<BeaconHandler> getBeaconHandler(osiSockAddr* responseFrom) = 0;
|
||||
|
||||
virtual void configure(epics::pvData::PVStructure::shared_pointer configuration) = 0;
|
||||
virtual void flush() = 0;
|
||||
virtual void poll() = 0;
|
||||
};
|
||||
|
||||
epicsShareExtern ClientContextImpl::shared_pointer createClientContextImpl();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CLIENTCONTEXTIMPL_H_ */
|
||||
7
src/rpcClient/Makefile
Normal file
7
src/rpcClient/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/rpcClient
|
||||
|
||||
INC += rpcClient.h
|
||||
|
||||
LIBSRCS += rpcClient.cpp
|
||||
309
src/rpcClient/rpcClient.cpp
Normal file
309
src/rpcClient/rpcClient.cpp
Normal file
@@ -0,0 +1,309 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvAccessCPP is distributed subject to a Software License Agreement
|
||||
* found in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/event.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/clientFactory.h>
|
||||
#include <pv/logger.h>
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
#include "rpcClient.h"
|
||||
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
|
||||
namespace epics
|
||||
{
|
||||
|
||||
namespace pvAccess
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// copied from eget/pvutils. This is a lot of boilerplate and needs refactoring to common code.
|
||||
|
||||
class ChannelRequesterImpl :
|
||||
public epics::pvAccess::ChannelRequester
|
||||
{
|
||||
private:
|
||||
epics::pvData::Event m_event;
|
||||
|
||||
public:
|
||||
virtual epics::pvData::String getRequesterName();
|
||||
virtual void message(epics::pvData::String const & message, epics::pvData::MessageType messageType);
|
||||
|
||||
virtual void channelCreated(const epics::pvData::Status& status, epics::pvAccess::Channel::shared_pointer const & channel);
|
||||
virtual void channelStateChange(epics::pvAccess::Channel::shared_pointer const & channel, epics::pvAccess::Channel::ConnectionState connectionState);
|
||||
|
||||
bool waitUntilConnected(double timeOut);
|
||||
};
|
||||
|
||||
class ChannelRPCRequesterImpl : public ChannelRPCRequester
|
||||
{
|
||||
private:
|
||||
ChannelRPC::shared_pointer m_channelRPC;
|
||||
Mutex m_pointerMutex;
|
||||
Event m_event;
|
||||
Event m_connectionEvent;
|
||||
String m_channelName;
|
||||
|
||||
public:
|
||||
epics::pvData::PVStructure::shared_pointer response;
|
||||
ChannelRPCRequesterImpl(String channelName) : m_channelName(channelName) {}
|
||||
|
||||
virtual String getRequesterName()
|
||||
{
|
||||
return "ChannelRPCRequesterImpl";
|
||||
}
|
||||
|
||||
virtual void message(String const & message, MessageType messageType)
|
||||
{
|
||||
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
|
||||
}
|
||||
|
||||
virtual void channelRPCConnect(const epics::pvData::Status& status,ChannelRPC::shared_pointer const & channelRPC)
|
||||
{
|
||||
if (status.isSuccess())
|
||||
{
|
||||
// show warning
|
||||
if (!status.isOK())
|
||||
{
|
||||
std::cerr << "[" << m_channelName << "] channel RPC create: " << status.toString() << std::endl;
|
||||
}
|
||||
|
||||
// assign smart pointers
|
||||
{
|
||||
Lock lock(m_pointerMutex);
|
||||
m_channelRPC = channelRPC;
|
||||
}
|
||||
|
||||
m_connectionEvent.signal();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "[" << m_channelName << "] failed to create channel get: " << status.toString() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void requestDone (const epics::pvData::Status &status, epics::pvData::PVStructure::shared_pointer const &pvResponse)
|
||||
{
|
||||
if (status.isSuccess())
|
||||
{
|
||||
// show warning
|
||||
if (!status.isOK())
|
||||
{
|
||||
std::cerr << "[" << m_channelName << "] channel RPC: " << status.toString() << std::endl;
|
||||
}
|
||||
|
||||
// access smart pointers
|
||||
{
|
||||
Lock lock(m_pointerMutex);
|
||||
|
||||
response = pvResponse;
|
||||
// this is OK since calle holds also owns it
|
||||
m_channelRPC.reset();
|
||||
}
|
||||
|
||||
m_event.signal();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "[" << m_channelName << "] failed to RPC: " << status.toString() << std::endl;
|
||||
{
|
||||
Lock lock(m_pointerMutex);
|
||||
// this is OK since caller holds also owns it
|
||||
m_channelRPC.reset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
void request(epics::pvData::PVStructure::shared_pointer const &pvRequest)
|
||||
{
|
||||
Lock lock(m_pointerMutex);
|
||||
m_channelRPC->request(pvRequest, false);
|
||||
}
|
||||
*/
|
||||
|
||||
bool waitUntilRPC(double timeOut)
|
||||
{
|
||||
return m_event.wait(timeOut);
|
||||
}
|
||||
|
||||
bool waitUntilConnected(double timeOut)
|
||||
{
|
||||
return m_connectionEvent.wait(timeOut);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
String ChannelRequesterImpl::getRequesterName()
|
||||
{
|
||||
return "ChannelRequesterImpl";
|
||||
}
|
||||
|
||||
void ChannelRequesterImpl::message(String const & message, MessageType messageType)
|
||||
{
|
||||
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl;
|
||||
}
|
||||
|
||||
void ChannelRequesterImpl::channelCreated(const epics::pvData::Status& status, Channel::shared_pointer const & channel)
|
||||
{
|
||||
if (status.isSuccess())
|
||||
{
|
||||
// show warning
|
||||
if (!status.isOK())
|
||||
{
|
||||
std::cerr << "[" << channel->getChannelName() << "] channel create: " << status.toString() << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "[" << channel->getChannelName() << "] failed to create a channel: " << status.toString() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelRequesterImpl::channelStateChange(Channel::shared_pointer const & /*channel*/, Channel::ConnectionState connectionState)
|
||||
{
|
||||
if (connectionState == Channel::CONNECTED)
|
||||
{
|
||||
m_event.signal();
|
||||
}
|
||||
/*
|
||||
else if (connectionState != Channel::DESTROYED)
|
||||
{
|
||||
std::cerr << "[" << channel->getChannelName() << "] channel state change: " << Channel::ConnectionStateNames[connectionState] << std::endl;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
bool ChannelRequesterImpl::waitUntilConnected(double timeOut)
|
||||
{
|
||||
return m_event.wait(timeOut);
|
||||
}
|
||||
|
||||
|
||||
class RPCClientImpl: public RPCClient
|
||||
{
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(RPCClientImpl);
|
||||
|
||||
RPCClientImpl(const std::string & serviceName)
|
||||
: m_serviceName(serviceName), m_connected(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual PVStructure::shared_pointer request(PVStructure::shared_pointer pvRequest, double timeOut);
|
||||
|
||||
private:
|
||||
void init()
|
||||
{
|
||||
using namespace std::tr1;
|
||||
m_provider = getChannelAccess()->getProvider("pva");
|
||||
|
||||
shared_ptr<ChannelRequesterImpl> channelRequesterImpl(new ChannelRequesterImpl());
|
||||
m_channelRequesterImpl = channelRequesterImpl;
|
||||
m_channel = m_provider->createChannel(m_serviceName, channelRequesterImpl);
|
||||
}
|
||||
|
||||
bool connect(double timeOut)
|
||||
{
|
||||
init();
|
||||
m_connected = m_channelRequesterImpl->waitUntilConnected(timeOut);
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
std::string m_serviceName;
|
||||
ChannelProvider::shared_pointer m_provider;
|
||||
std::tr1::shared_ptr<ChannelRequesterImpl> m_channelRequesterImpl;
|
||||
Channel::shared_pointer m_channel;
|
||||
bool m_connected;
|
||||
};
|
||||
|
||||
|
||||
PVStructure::shared_pointer RPCClientImpl::request(PVStructure::shared_pointer pvRequest, double timeOut)
|
||||
{
|
||||
using namespace std::tr1;
|
||||
|
||||
PVStructure::shared_pointer response;
|
||||
|
||||
bool allOK = true;
|
||||
|
||||
//ClientFactory::start();
|
||||
|
||||
if (m_connected || connect(timeOut))
|
||||
{
|
||||
shared_ptr<ChannelRPCRequesterImpl> rpcRequesterImpl(new ChannelRPCRequesterImpl(m_channel->getChannelName()));
|
||||
ChannelRPC::shared_pointer channelRPC = m_channel->createChannelRPC(rpcRequesterImpl, pvRequest);
|
||||
|
||||
if (rpcRequesterImpl->waitUntilConnected(timeOut))
|
||||
{
|
||||
channelRPC->request(pvRequest, true);
|
||||
allOK &= rpcRequesterImpl->waitUntilRPC(timeOut);
|
||||
response = rpcRequesterImpl->response;
|
||||
}
|
||||
else
|
||||
{
|
||||
allOK = false;
|
||||
m_channel->destroy();
|
||||
m_connected = false;
|
||||
std::string errMsg = "[" + m_channel->getChannelName() + "] RPC create timeout";
|
||||
throw epics::pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, errMsg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
allOK = false;
|
||||
m_channel->destroy();
|
||||
m_connected = false;
|
||||
std::string errMsg = "[" + m_channel->getChannelName() + "] connection timeout";
|
||||
throw epics::pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, errMsg);
|
||||
}
|
||||
|
||||
//ClientFactory::stop();
|
||||
|
||||
if (!allOK)
|
||||
{
|
||||
throw epics::pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, "RPC request failed");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RPCClient::shared_pointer RPCClientFactory::create(const std::string & serviceName)
|
||||
{
|
||||
return RPCClient::shared_pointer(new RPCClientImpl(serviceName));
|
||||
}
|
||||
|
||||
|
||||
PVStructure::shared_pointer sendRequest(const std::string & serviceName,
|
||||
PVStructure::shared_pointer queryRequest,
|
||||
double timeOut)
|
||||
{
|
||||
RPCClient::shared_pointer client = RPCClientFactory::create(serviceName);
|
||||
|
||||
return client->request(queryRequest, timeOut);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
84
src/rpcClient/rpcClient.h
Normal file
84
src/rpcClient/rpcClient.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvAccessCPP is distributed subject to a Software License Agreement
|
||||
* found in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#ifndef RPCCLIENT_H
|
||||
#define RPCCLIENT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define rpcClientEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
#include <pv/pvData.h>
|
||||
#ifdef rpcClientEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef rpcClientEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics
|
||||
{
|
||||
|
||||
namespace pvAccess
|
||||
{
|
||||
/**
|
||||
* RPCClient is an interface class that is used by a service client.
|
||||
*
|
||||
*/
|
||||
class epicsShareClass RPCClient
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(RPCClient);
|
||||
|
||||
|
||||
/**
|
||||
* Sends a request and wait for the response or until timeout occurs.
|
||||
* This method will also wait for client to connect, if necessary.
|
||||
*
|
||||
* @param pvArgument the argument for the rpc
|
||||
* @param timeout the time in seconds to wait for the response
|
||||
* @return request response.
|
||||
*/
|
||||
virtual epics::pvData::PVStructure::shared_pointer request(epics::pvData::PVStructure::shared_pointer pvRequest,
|
||||
double timeOut) = 0;
|
||||
|
||||
virtual ~RPCClient() {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class RPCClientFactory
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a RPCClient.
|
||||
*
|
||||
* @param serviceName the service name
|
||||
* @return the RPCClient interface
|
||||
*/
|
||||
static RPCClient::shared_pointer create(const std::string & serviceName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs complete blocking RPC call, opening a channel and connecting to the
|
||||
* service and sending the request.
|
||||
* The PVStructure sent on connection is null.
|
||||
*
|
||||
* @param serviceName the name of the service to connect to
|
||||
* @param request the request sent to the service
|
||||
* @return the result of the RPC call.
|
||||
*/
|
||||
epics::pvData::PVStructure::shared_pointer sendRequest(const std::string & serviceName,
|
||||
epics::pvData::PVStructure::shared_pointer request, double timeOut);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
9
src/rpcService/Makefile
Normal file
9
src/rpcService/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/rpcService
|
||||
|
||||
INC += rpcService.h
|
||||
INC += rpcServer.h
|
||||
|
||||
LIBSRCS += rpcService.cpp
|
||||
LIBSRCS += rpcServer.cpp
|
||||
476
src/rpcService/rpcServer.cpp
Normal file
476
src/rpcService/rpcServer.cpp
Normal file
@@ -0,0 +1,476 @@
|
||||
/**
|
||||
* 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 <stdexcept>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/rpcServer.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics { namespace pvAccess {
|
||||
|
||||
|
||||
class ChannelRPCServiceImpl : public ChannelRPC
|
||||
{
|
||||
private:
|
||||
ChannelRPCRequester::shared_pointer m_channelRPCRequester;
|
||||
RPCService::shared_pointer m_rpcService;
|
||||
|
||||
public:
|
||||
ChannelRPCServiceImpl(
|
||||
ChannelRPCRequester::shared_pointer const & channelRPCRequester,
|
||||
RPCService::shared_pointer const & rpcService) :
|
||||
m_channelRPCRequester(channelRPCRequester),
|
||||
m_rpcService(rpcService)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ChannelRPCServiceImpl()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void processRequest(epics::pvData::PVStructure::shared_pointer const & pvArgument, bool lastRequest)
|
||||
{
|
||||
epics::pvData::PVStructure::shared_pointer result;
|
||||
Status status = Status::Ok;
|
||||
bool ok = true;
|
||||
try
|
||||
{
|
||||
result = m_rpcService->request(pvArgument);
|
||||
}
|
||||
catch (RPCRequestException& rre)
|
||||
{
|
||||
status = Status(rre.getStatus(), rre.what());
|
||||
ok = false;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
status = Status(Status::STATUSTYPE_FATAL, ex.what());
|
||||
ok = false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// handle user unexpected errors
|
||||
status = Status(Status::STATUSTYPE_FATAL, "Unexpected exception caught while calling RPCService.request(PVStructure).");
|
||||
ok = false;
|
||||
}
|
||||
|
||||
// check null result
|
||||
if (ok && result.get() == 0)
|
||||
{
|
||||
status = Status(Status::STATUSTYPE_FATAL, "RPCService.request(PVStructure) returned null.");
|
||||
}
|
||||
|
||||
m_channelRPCRequester->requestDone(status, result);
|
||||
|
||||
if (lastRequest)
|
||||
destroy();
|
||||
|
||||
}
|
||||
|
||||
virtual void request(epics::pvData::PVStructure::shared_pointer const & pvArgument, bool lastRequest)
|
||||
{
|
||||
processRequest(pvArgument, lastRequest);
|
||||
}
|
||||
|
||||
virtual void destroy()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual void lock()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual void unlock()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class RPCChannel :
|
||||
public virtual Channel
|
||||
{
|
||||
private:
|
||||
|
||||
static Status notSupportedStatus;
|
||||
static Status destroyedStatus;
|
||||
|
||||
AtomicBoolean m_destroyed;
|
||||
|
||||
ChannelProvider::shared_pointer m_provider;
|
||||
String m_channelName;
|
||||
ChannelRequester::shared_pointer m_channelRequester;
|
||||
|
||||
RPCService::shared_pointer m_rpcService;
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(RPCChannel);
|
||||
|
||||
RPCChannel(
|
||||
ChannelProvider::shared_pointer const & provider,
|
||||
String const & channelName,
|
||||
ChannelRequester::shared_pointer const & channelRequester,
|
||||
RPCService::shared_pointer const & rpcService) :
|
||||
m_provider(provider),
|
||||
m_channelName(channelName),
|
||||
m_channelRequester(channelRequester),
|
||||
m_rpcService(rpcService)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~RPCChannel()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
virtual std::tr1::shared_ptr<ChannelProvider> getProvider()
|
||||
{
|
||||
return m_provider;
|
||||
}
|
||||
|
||||
virtual epics::pvData::String getRemoteAddress()
|
||||
{
|
||||
// local
|
||||
return getChannelName();
|
||||
}
|
||||
|
||||
virtual ConnectionState getConnectionState()
|
||||
{
|
||||
return isConnected() ?
|
||||
Channel::CONNECTED :
|
||||
Channel::DESTROYED;
|
||||
}
|
||||
|
||||
virtual epics::pvData::String getChannelName()
|
||||
{
|
||||
return m_channelName;
|
||||
}
|
||||
|
||||
virtual std::tr1::shared_ptr<ChannelRequester> getChannelRequester()
|
||||
{
|
||||
return m_channelRequester;
|
||||
}
|
||||
|
||||
virtual bool isConnected()
|
||||
{
|
||||
return !m_destroyed.get();
|
||||
}
|
||||
|
||||
|
||||
virtual AccessRights getAccessRights(epics::pvData::PVField::shared_pointer const & /*pvField*/)
|
||||
{
|
||||
return none;
|
||||
}
|
||||
|
||||
virtual void getField(GetFieldRequester::shared_pointer const & requester,epics::pvData::String const & /*subField*/)
|
||||
{
|
||||
requester->getDone(notSupportedStatus, epics::pvData::Field::shared_pointer());
|
||||
}
|
||||
|
||||
virtual ChannelProcess::shared_pointer createChannelProcess(
|
||||
ChannelProcessRequester::shared_pointer const & channelProcessRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
|
||||
{
|
||||
ChannelProcess::shared_pointer nullPtr;
|
||||
channelProcessRequester->channelProcessConnect(notSupportedStatus, nullPtr);
|
||||
return nullPtr;
|
||||
}
|
||||
|
||||
virtual ChannelGet::shared_pointer createChannelGet(
|
||||
ChannelGetRequester::shared_pointer const & channelGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
|
||||
{
|
||||
ChannelGet::shared_pointer nullPtr;
|
||||
channelGetRequester->channelGetConnect(notSupportedStatus, nullPtr,
|
||||
epics::pvData::PVStructure::shared_pointer(), epics::pvData::BitSet::shared_pointer());
|
||||
return nullPtr;
|
||||
}
|
||||
|
||||
virtual ChannelPut::shared_pointer createChannelPut(
|
||||
ChannelPutRequester::shared_pointer const & channelPutRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
|
||||
{
|
||||
ChannelPut::shared_pointer nullPtr;
|
||||
channelPutRequester->channelPutConnect(notSupportedStatus, nullPtr,
|
||||
epics::pvData::PVStructure::shared_pointer(), epics::pvData::BitSet::shared_pointer());
|
||||
return nullPtr;
|
||||
}
|
||||
|
||||
|
||||
virtual ChannelPutGet::shared_pointer createChannelPutGet(
|
||||
ChannelPutGetRequester::shared_pointer const & channelPutGetRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
|
||||
{
|
||||
ChannelPutGet::shared_pointer nullPtr;
|
||||
epics::pvData::PVStructure::shared_pointer nullStructure;
|
||||
channelPutGetRequester->channelPutGetConnect(notSupportedStatus, nullPtr, nullStructure, nullStructure);
|
||||
return nullPtr;
|
||||
}
|
||||
|
||||
virtual ChannelRPC::shared_pointer createChannelRPC(
|
||||
ChannelRPCRequester::shared_pointer const & channelRPCRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
|
||||
{
|
||||
// nothing expected to be in pvRequest
|
||||
|
||||
if (channelRPCRequester.get() == 0)
|
||||
throw std::invalid_argument("channelRPCRequester == null");
|
||||
|
||||
if (m_destroyed.get())
|
||||
{
|
||||
ChannelRPC::shared_pointer nullPtr;
|
||||
channelRPCRequester->channelRPCConnect(destroyedStatus, nullPtr);
|
||||
return nullPtr;
|
||||
}
|
||||
|
||||
ChannelRPC::shared_pointer channelRPCImpl(new ChannelRPCServiceImpl(channelRPCRequester, m_rpcService));
|
||||
channelRPCRequester->channelRPCConnect(Status::Ok, channelRPCImpl);
|
||||
return channelRPCImpl;
|
||||
}
|
||||
|
||||
virtual epics::pvData::Monitor::shared_pointer createMonitor(
|
||||
epics::pvData::MonitorRequester::shared_pointer const & monitorRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
|
||||
{
|
||||
epics::pvData::Monitor::shared_pointer nullPtr;
|
||||
monitorRequester->monitorConnect(notSupportedStatus, nullPtr, epics::pvData::Structure::shared_pointer());
|
||||
return nullPtr;
|
||||
}
|
||||
|
||||
virtual ChannelArray::shared_pointer createChannelArray(
|
||||
ChannelArrayRequester::shared_pointer const & channelArrayRequester,
|
||||
epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/)
|
||||
{
|
||||
ChannelArray::shared_pointer nullPtr;
|
||||
channelArrayRequester->channelArrayConnect(notSupportedStatus, nullPtr, epics::pvData::PVArray::shared_pointer());
|
||||
return nullPtr;
|
||||
}
|
||||
|
||||
|
||||
virtual void printInfo()
|
||||
{
|
||||
std::cout << "RPCChannel: " << getChannelName() << " [" << Channel::ConnectionStateNames[getConnectionState()] << "]" << std::endl;
|
||||
}
|
||||
|
||||
virtual void printInfo(epics::pvData::StringBuilder out)
|
||||
{
|
||||
*out += "RPCChannel: ";
|
||||
*out += getChannelName();
|
||||
*out += " [";
|
||||
*out += Channel::ConnectionStateNames[getConnectionState()];
|
||||
*out += "]";
|
||||
}
|
||||
|
||||
virtual String getRequesterName()
|
||||
{
|
||||
return getChannelName();
|
||||
}
|
||||
|
||||
virtual void message(String const & message,MessageType messageType)
|
||||
{
|
||||
// just delegate
|
||||
m_channelRequester->message(message, messageType);
|
||||
}
|
||||
|
||||
virtual void destroy()
|
||||
{
|
||||
m_destroyed.set();
|
||||
}
|
||||
};
|
||||
|
||||
Status RPCChannel::notSupportedStatus(Status::STATUSTYPE_ERROR, "only channelRPC requests are supported by this channel");
|
||||
Status RPCChannel::destroyedStatus(Status::STATUSTYPE_ERROR, "channel destroyed");
|
||||
|
||||
|
||||
|
||||
class RPCChannelProvider :
|
||||
public virtual ChannelProvider,
|
||||
public virtual ChannelFind,
|
||||
public std::tr1::enable_shared_from_this<RPCChannelProvider> {
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(RPCChannelProvider);
|
||||
|
||||
static String PROVIDER_NAME;
|
||||
|
||||
static Status noSuchChannelStatus;
|
||||
|
||||
// TODO thread pool support
|
||||
|
||||
RPCChannelProvider() {
|
||||
}
|
||||
|
||||
virtual String getProviderName() {
|
||||
return PROVIDER_NAME;
|
||||
}
|
||||
|
||||
virtual std::tr1::shared_ptr<ChannelProvider> getChannelProvider()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
virtual void cancelChannelFind() {}
|
||||
|
||||
virtual void destroy() {}
|
||||
|
||||
virtual ChannelFind::shared_pointer channelFind(epics::pvData::String const & channelName,
|
||||
ChannelFindRequester::shared_pointer const & channelFindRequester)
|
||||
{
|
||||
bool found;
|
||||
{
|
||||
Lock guard(m_mutex);
|
||||
found = (m_services.find(channelName) != m_services.end());
|
||||
}
|
||||
ChannelFind::shared_pointer thisPtr(shared_from_this());
|
||||
channelFindRequester->channelFindResult(Status::Ok, thisPtr, found);
|
||||
return thisPtr;
|
||||
}
|
||||
|
||||
|
||||
virtual Channel::shared_pointer createChannel(
|
||||
epics::pvData::String const & channelName,
|
||||
ChannelRequester::shared_pointer const & channelRequester,
|
||||
short /*priority*/)
|
||||
{
|
||||
RPCServiceMap::const_iterator iter;
|
||||
{
|
||||
Lock guard(m_mutex);
|
||||
iter = m_services.find(channelName);
|
||||
}
|
||||
|
||||
if (iter == m_services.end())
|
||||
{
|
||||
Channel::shared_pointer nullChannel;
|
||||
channelRequester->channelCreated(noSuchChannelStatus, nullChannel);
|
||||
return nullChannel;
|
||||
}
|
||||
|
||||
Channel::shared_pointer rpcChannel(new RPCChannel(
|
||||
shared_from_this(),
|
||||
channelName,
|
||||
channelRequester,
|
||||
iter->second));
|
||||
channelRequester->channelCreated(Status::Ok, rpcChannel);
|
||||
return rpcChannel;
|
||||
}
|
||||
|
||||
virtual Channel::shared_pointer createChannel(
|
||||
epics::pvData::String const & /*channelName*/,
|
||||
ChannelRequester::shared_pointer const & /*channelRequester*/,
|
||||
short /*priority*/,
|
||||
epics::pvData::String const & /*address*/)
|
||||
{
|
||||
// this will never get called by the pvAccess server
|
||||
throw std::runtime_error("not supported");
|
||||
}
|
||||
|
||||
void registerService(String const & serviceName, RPCService::shared_pointer const & service)
|
||||
{
|
||||
Lock guard(m_mutex);
|
||||
m_services[serviceName] = service;
|
||||
}
|
||||
|
||||
void unregisterService(String const & serviceName)
|
||||
{
|
||||
Lock guard(m_mutex);
|
||||
m_services.erase(serviceName);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::map<String, RPCService::shared_pointer> RPCServiceMap;
|
||||
RPCServiceMap m_services;
|
||||
epics::pvData::Mutex m_mutex;
|
||||
};
|
||||
|
||||
String RPCChannelProvider::PROVIDER_NAME("rpcService");
|
||||
Status RPCChannelProvider::noSuchChannelStatus(Status::STATUSTYPE_ERROR, "no such channel");
|
||||
|
||||
|
||||
|
||||
class RPCChannelProviderFactory : public ChannelProviderFactory
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(RPCChannelProviderFactory);
|
||||
|
||||
RPCChannelProviderFactory() :
|
||||
m_channelProviderImpl(new RPCChannelProvider())
|
||||
{
|
||||
}
|
||||
|
||||
virtual epics::pvData::String getFactoryName()
|
||||
{
|
||||
return RPCChannelProvider::PROVIDER_NAME;
|
||||
}
|
||||
|
||||
virtual ChannelProvider::shared_pointer sharedInstance()
|
||||
{
|
||||
return m_channelProviderImpl;
|
||||
}
|
||||
|
||||
virtual ChannelProvider::shared_pointer newInstance()
|
||||
{
|
||||
return ChannelProvider::shared_pointer(new RPCChannelProvider());
|
||||
}
|
||||
|
||||
private:
|
||||
RPCChannelProvider::shared_pointer m_channelProviderImpl;
|
||||
};
|
||||
|
||||
|
||||
RPCServer::RPCServer()
|
||||
{
|
||||
// TODO factory is never deregistered, multiple RPCServer instances create multiple factories, etc.
|
||||
m_channelProviderFactory.reset(new RPCChannelProviderFactory());
|
||||
registerChannelProviderFactory(m_channelProviderFactory);
|
||||
|
||||
m_channelProviderImpl = m_channelProviderFactory->sharedInstance();
|
||||
|
||||
m_serverContext = ServerContextImpl::create();
|
||||
m_serverContext->setChannelProviderName(m_channelProviderImpl->getProviderName());
|
||||
|
||||
m_serverContext->initialize(getChannelAccess());
|
||||
}
|
||||
|
||||
RPCServer::~RPCServer()
|
||||
{
|
||||
// multiple destroy call is OK
|
||||
destroy();
|
||||
}
|
||||
|
||||
void RPCServer::printInfo()
|
||||
{
|
||||
std::cout << m_serverContext->getVersion().getVersionString() << std::endl;
|
||||
m_serverContext->printInfo();
|
||||
}
|
||||
|
||||
void RPCServer::run(int seconds)
|
||||
{
|
||||
m_serverContext->run(seconds);
|
||||
}
|
||||
|
||||
void RPCServer::destroy()
|
||||
{
|
||||
m_serverContext->destroy();
|
||||
}
|
||||
|
||||
void RPCServer::registerService(String const & serviceName, RPCService::shared_pointer const & service)
|
||||
{
|
||||
std::tr1::dynamic_pointer_cast<RPCChannelProvider>(m_channelProviderImpl)->registerService(serviceName, service);
|
||||
}
|
||||
|
||||
void RPCServer::unregisterService(String const & serviceName)
|
||||
{
|
||||
std::tr1::dynamic_pointer_cast<RPCChannelProvider>(m_channelProviderImpl)->unregisterService(serviceName);
|
||||
}
|
||||
|
||||
}}
|
||||
64
src/rpcService/rpcServer.h
Normal file
64
src/rpcService/rpcServer.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef RPCSERVER_H
|
||||
#define RPCSERVER_H
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define rpcServerEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#ifdef rpcServerEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef rpcServerEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/rpcService.h>
|
||||
#include <pv/serverContext.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvAccess {
|
||||
|
||||
class epicsShareClass RPCServer {
|
||||
private:
|
||||
|
||||
ServerContextImpl::shared_pointer m_serverContext;
|
||||
ChannelProviderFactory::shared_pointer m_channelProviderFactory;
|
||||
ChannelProvider::shared_pointer m_channelProviderImpl;
|
||||
|
||||
// TODO no thread poll implementation
|
||||
|
||||
public:
|
||||
POINTER_DEFINITIONS(RPCServer);
|
||||
|
||||
RPCServer();
|
||||
|
||||
virtual ~RPCServer();
|
||||
|
||||
void registerService(epics::pvData::String const & serviceName, RPCService::shared_pointer const & service);
|
||||
|
||||
void unregisterService(epics::pvData::String const & serviceName);
|
||||
|
||||
void run(int seconds = 0);
|
||||
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Display basic information about the context.
|
||||
*/
|
||||
void printInfo();
|
||||
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
|
||||
#endif /* RPCSERVER_H */
|
||||
8
src/rpcService/rpcService.cpp
Normal file
8
src/rpcService/rpcService.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/rpcService.h>
|
||||
61
src/rpcService/rpcService.h
Normal file
61
src/rpcService/rpcService.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef RPCSERVICE_H
|
||||
#define RPCSERVICE_H
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define rpcServiceEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
#include <pv/status.h>
|
||||
|
||||
#ifdef rpcServiceEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef rpcServiceEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvAccess {
|
||||
|
||||
class epicsShareClass RPCRequestException : public std::runtime_error {
|
||||
public:
|
||||
|
||||
RPCRequestException(epics::pvData::Status::StatusType status, epics::pvData::String const & message) :
|
||||
std::runtime_error(message), m_status(status)
|
||||
{
|
||||
}
|
||||
|
||||
epics::pvData::Status::StatusType getStatus() const {
|
||||
return m_status;
|
||||
}
|
||||
|
||||
private:
|
||||
epics::pvData::Status::StatusType m_status;
|
||||
};
|
||||
|
||||
|
||||
class epicsShareClass RPCService {
|
||||
public:
|
||||
POINTER_DEFINITIONS(RPCService);
|
||||
|
||||
virtual ~RPCService() {};
|
||||
|
||||
virtual epics::pvData::PVStructure::shared_pointer request(
|
||||
epics::pvData::PVStructure::shared_pointer const & args
|
||||
) throw (RPCRequestException) = 0;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* RPCSERVICE_H */
|
||||
17
src/server/Makefile
Normal file
17
src/server/Makefile
Normal file
@@ -0,0 +1,17 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/server
|
||||
|
||||
INC += serverContext.h
|
||||
INC += responseHandlers.h
|
||||
INC += serverChannelImpl.h
|
||||
INC += baseChannelRequester.h
|
||||
INC += beaconEmitter.h
|
||||
INC += beaconServerStatusProvider.h
|
||||
|
||||
LIBSRCS += responseHandlers.cpp
|
||||
LIBSRCS += serverContext.cpp
|
||||
LIBSRCS += serverChannelImpl.cpp
|
||||
LIBSRCS += baseChannelRequester.cpp
|
||||
LIBSRCS += beaconEmitter.cpp
|
||||
LIBSRCS += beaconServerStatusProvider.cpp
|
||||
140
src/server/baseChannelRequester.cpp
Normal file
140
src/server/baseChannelRequester.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* 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/baseChannelRequester.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
const Status BaseChannelRequester::okStatus = Status();
|
||||
const Status BaseChannelRequester::badCIDStatus = Status(Status::STATUSTYPE_ERROR, "bad channel id");
|
||||
const Status BaseChannelRequester::badIOIDStatus = Status(Status::STATUSTYPE_ERROR, "bad request id");
|
||||
const Status BaseChannelRequester::noReadACLStatus = Status(Status::STATUSTYPE_ERROR, "no read access");
|
||||
const Status BaseChannelRequester::noWriteACLStatus = Status(Status::STATUSTYPE_ERROR, "no write access");
|
||||
const Status BaseChannelRequester::noProcessACLStatus = Status(Status::STATUSTYPE_ERROR, "no process access");
|
||||
const Status BaseChannelRequester::otherRequestPendingStatus = Status(Status::STATUSTYPE_ERROR, "other request pending");
|
||||
|
||||
const int32 BaseChannelRequester::NULL_REQUEST = -1;
|
||||
|
||||
BaseChannelRequester::BaseChannelRequester(
|
||||
ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel,
|
||||
const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport) :
|
||||
_ioid(ioid),
|
||||
_transport(transport),
|
||||
_channel(channel),
|
||||
_context(context),
|
||||
_pendingRequest(BaseChannelRequester::NULL_REQUEST)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool BaseChannelRequester::startRequest(int32 qos)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
if (_pendingRequest != NULL_REQUEST)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_pendingRequest = qos;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseChannelRequester::stopRequest()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
_pendingRequest = NULL_REQUEST;
|
||||
}
|
||||
|
||||
int32 BaseChannelRequester::getPendingRequest()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
return _pendingRequest;
|
||||
}
|
||||
|
||||
String BaseChannelRequester::getRequesterName()
|
||||
{
|
||||
std::stringstream name;
|
||||
name << typeid(*_transport).name() << "/" << _ioid;
|
||||
return name.str();
|
||||
}
|
||||
|
||||
void BaseChannelRequester::message(String const & message, epics::pvData::MessageType messageType)
|
||||
{
|
||||
BaseChannelRequester::message(_transport, _ioid, message, messageType);
|
||||
}
|
||||
|
||||
void BaseChannelRequester::message(Transport::shared_pointer const & transport, const pvAccessID ioid, const String message, const MessageType messageType)
|
||||
{
|
||||
TransportSender::shared_pointer sender(new BaseChannelRequesterMessageTransportSender(ioid, message, messageType));
|
||||
transport->enqueueSendRequest(sender);
|
||||
}
|
||||
|
||||
void BaseChannelRequester::sendFailureMessage(const int8 command, Transport::shared_pointer const & transport, const pvAccessID ioid, const int8 qos, const Status status)
|
||||
{
|
||||
TransportSender::shared_pointer sender(new BaseChannelRequesterFailureMessageTransportSender(command, transport, ioid, qos, status));
|
||||
transport->enqueueSendRequest(sender);
|
||||
}
|
||||
|
||||
BaseChannelRequesterMessageTransportSender::BaseChannelRequesterMessageTransportSender(const pvAccessID ioid, const String message,const epics::pvData::MessageType messageType):
|
||||
_ioid(ioid),
|
||||
_message(message),
|
||||
_messageType(messageType)
|
||||
{
|
||||
}
|
||||
|
||||
void BaseChannelRequesterMessageTransportSender::send(ByteBuffer* buffer, TransportSendControl* control)
|
||||
{
|
||||
control->startMessage((int8)18, sizeof(int32)/sizeof(int8) + 1);
|
||||
buffer->putInt(_ioid);
|
||||
buffer->putByte((int8)_messageType);
|
||||
epics::pvData::SerializeHelper::serializeString(_message, buffer, control);
|
||||
}
|
||||
|
||||
void BaseChannelRequesterMessageTransportSender::lock()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
void BaseChannelRequesterMessageTransportSender::unlock()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
BaseChannelRequesterFailureMessageTransportSender::BaseChannelRequesterFailureMessageTransportSender(const int8 command,
|
||||
Transport::shared_pointer const & transport, const pvAccessID ioid, const int8 qos, const Status& status) :
|
||||
_command(command),
|
||||
_ioid(ioid),
|
||||
_qos(qos),
|
||||
_status(status),
|
||||
_transport(transport)
|
||||
{
|
||||
}
|
||||
|
||||
void BaseChannelRequesterFailureMessageTransportSender::send(ByteBuffer* buffer, TransportSendControl* control)
|
||||
{
|
||||
control->startMessage(_command, sizeof(int32)/sizeof(int8) + 1);
|
||||
buffer->putInt(_ioid);
|
||||
buffer->put(_qos);
|
||||
_status.serialize(buffer, control);
|
||||
}
|
||||
|
||||
void BaseChannelRequesterFailureMessageTransportSender::lock()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
void BaseChannelRequesterFailureMessageTransportSender::unlock()
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
95
src/server/baseChannelRequester.h
Normal file
95
src/server/baseChannelRequester.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BASECHANNELREQUESTER_H_
|
||||
#define BASECHANNELREQUESTER_H_
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define baseChannelRequesterEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/requester.h>
|
||||
#include <pv/destroyable.h>
|
||||
|
||||
#ifdef baseChannelRequesterEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef baseChannelRequesterEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/serverChannelImpl.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class BaseChannelRequester : virtual public epics::pvData::Requester, public epics::pvData::Destroyable
|
||||
{
|
||||
public:
|
||||
BaseChannelRequester(ServerContextImpl::shared_pointer const & context, ServerChannelImpl::shared_pointer const & channel,
|
||||
const pvAccessID ioid, Transport::shared_pointer const & transport);
|
||||
virtual ~BaseChannelRequester() {};
|
||||
|
||||
bool startRequest(epics::pvData::int32 qos);
|
||||
void stopRequest();
|
||||
epics::pvData::int32 getPendingRequest();
|
||||
epics::pvData::String getRequesterName();
|
||||
void message(epics::pvData::String const & message, epics::pvData::MessageType messageType);
|
||||
static void message(Transport::shared_pointer const & transport, const pvAccessID ioid, const epics::pvData::String message, const epics::pvData::MessageType messageType);
|
||||
static void sendFailureMessage(const epics::pvData::int8 command, Transport::shared_pointer const & transport, const pvAccessID ioid, const epics::pvData::int8 qos, const epics::pvData::Status status);
|
||||
|
||||
static const epics::pvData::Status okStatus;
|
||||
static const epics::pvData::Status badCIDStatus;
|
||||
static const epics::pvData::Status badIOIDStatus;
|
||||
static const epics::pvData::Status noReadACLStatus;
|
||||
static const epics::pvData::Status noWriteACLStatus;
|
||||
static const epics::pvData::Status noProcessACLStatus;
|
||||
static const epics::pvData::Status otherRequestPendingStatus;
|
||||
protected:
|
||||
const pvAccessID _ioid;
|
||||
Transport::shared_pointer _transport;
|
||||
ServerChannelImpl::shared_pointer _channel;
|
||||
epics::pvData::Mutex _mutex;
|
||||
private:
|
||||
ServerContextImpl::shared_pointer _context;
|
||||
static const epics::pvData::int32 NULL_REQUEST;
|
||||
epics::pvData::int32 _pendingRequest;
|
||||
};
|
||||
|
||||
class BaseChannelRequesterMessageTransportSender : public TransportSender
|
||||
{
|
||||
public:
|
||||
BaseChannelRequesterMessageTransportSender(const pvAccessID _ioid, const epics::pvData::String message,const epics::pvData::MessageType messageType);
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
void lock();
|
||||
void unlock();
|
||||
private:
|
||||
const pvAccessID _ioid;
|
||||
const epics::pvData::String _message;
|
||||
const epics::pvData::MessageType _messageType;
|
||||
};
|
||||
|
||||
class BaseChannelRequesterFailureMessageTransportSender : public TransportSender
|
||||
{
|
||||
public:
|
||||
BaseChannelRequesterFailureMessageTransportSender(const epics::pvData::int8 command, Transport::shared_pointer const & transport, const pvAccessID ioid, const epics::pvData::int8 qos, const epics::pvData::Status& status);
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
const epics::pvData::int8 _command;
|
||||
const pvAccessID _ioid;
|
||||
const epics::pvData::int8 _qos;
|
||||
const epics::pvData::Status _status;
|
||||
Transport::shared_pointer _transport;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* BASECHANNELREQUESTER_H_ */
|
||||
150
src/server/beaconEmitter.cpp
Normal file
150
src/server/beaconEmitter.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <pv/beaconEmitter.h>
|
||||
#include <pv/serializationHelper.h>
|
||||
|
||||
#include <pv/logger.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <pv/serverContext.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics { namespace pvAccess {
|
||||
|
||||
const float BeaconEmitter::EPICS_PVA_MIN_BEACON_PERIOD = 1.0;
|
||||
const float BeaconEmitter::EPICS_PVA_MIN_BEACON_COUNT_LIMIT = 3.0;
|
||||
|
||||
//BeaconEmitter::BeaconEmitter(Transport::shared_pointer const & transport, ServerContextImpl::shared_pointer const & context) :
|
||||
BeaconEmitter::BeaconEmitter(Transport::shared_pointer const & transport, std::tr1::shared_ptr<ServerContextImpl>& context) :
|
||||
_transport(transport),
|
||||
_beaconSequenceID(0),
|
||||
_startupTime(),
|
||||
_fastBeaconPeriod(std::max(context->getBeaconPeriod(), EPICS_PVA_MIN_BEACON_PERIOD)),
|
||||
_slowBeaconPeriod(std::max(180.0, _fastBeaconPeriod)), // TODO configurable
|
||||
_beaconCountLimit((int16)std::max(10.0f, EPICS_PVA_MIN_BEACON_COUNT_LIMIT)), // TODO configurable
|
||||
_serverAddress(*(context->getServerInetAddress())),
|
||||
_serverPort(context->getServerPort()),
|
||||
_serverStatusProvider(context->getBeaconServerStatusProvider()),
|
||||
_timer(context->getTimer())
|
||||
{
|
||||
_startupTime.getCurrent();
|
||||
}
|
||||
|
||||
BeaconEmitter::BeaconEmitter(Transport::shared_pointer const & transport, const osiSockAddr& serverAddress) :
|
||||
_transport(transport),
|
||||
_beaconSequenceID(0),
|
||||
_startupTime(),
|
||||
_fastBeaconPeriod(EPICS_PVA_MIN_BEACON_PERIOD),
|
||||
_slowBeaconPeriod(180.0),
|
||||
_beaconCountLimit(10),
|
||||
_serverAddress(serverAddress),
|
||||
_serverPort(serverAddress.ia.sin_port),
|
||||
_serverStatusProvider(),
|
||||
_timer(new Timer("pvAccess-server timer", lowPriority))
|
||||
{
|
||||
_startupTime.getCurrent();
|
||||
}
|
||||
|
||||
BeaconEmitter::~BeaconEmitter()
|
||||
{
|
||||
// shared_from_this is not yet allows in destructor
|
||||
// be sure to call destroy() first !!!
|
||||
// destroy();
|
||||
}
|
||||
|
||||
void BeaconEmitter::lock()
|
||||
{
|
||||
//noop
|
||||
}
|
||||
|
||||
void BeaconEmitter::unlock()
|
||||
{
|
||||
//noop
|
||||
}
|
||||
|
||||
void BeaconEmitter::send(ByteBuffer* buffer, TransportSendControl* control)
|
||||
{
|
||||
// get server status
|
||||
PVField::shared_pointer serverStatus;
|
||||
if(_serverStatusProvider.get())
|
||||
{
|
||||
try
|
||||
{
|
||||
serverStatus = _serverStatusProvider->getServerStatusData();
|
||||
}
|
||||
catch (...) {
|
||||
// we have to proctect internal code from external implementation...
|
||||
LOG(logLevelDebug, "BeaconServerStatusProvider implementation thrown an exception.");
|
||||
}
|
||||
}
|
||||
|
||||
// send beacon
|
||||
control->startMessage((int8)0, (sizeof(int16)+2*sizeof(int32)+128+sizeof(int16))/sizeof(int8));
|
||||
|
||||
buffer->putShort(_beaconSequenceID);
|
||||
buffer->putLong((int64)_startupTime.getSecondsPastEpoch());
|
||||
buffer->putInt((int32)_startupTime.getNanoSeconds());
|
||||
|
||||
// NOTE: is it possible (very likely) that address is any local address ::ffff:0.0.0.0
|
||||
encodeAsIPv6Address(buffer, &_serverAddress);
|
||||
buffer->putShort((int16)_serverPort);
|
||||
|
||||
if (serverStatus)
|
||||
{
|
||||
// introspection interface + data
|
||||
serverStatus->getField()->serialize(buffer, control);
|
||||
serverStatus->serialize(buffer, control);
|
||||
}
|
||||
else
|
||||
{
|
||||
SerializationHelper::serializeNullField(buffer, control);
|
||||
}
|
||||
control->flush(true);
|
||||
|
||||
// increment beacon sequence ID
|
||||
_beaconSequenceID++;
|
||||
|
||||
reschedule();
|
||||
}
|
||||
|
||||
void BeaconEmitter::timerStopped()
|
||||
{
|
||||
//noop
|
||||
}
|
||||
|
||||
void BeaconEmitter::destroy()
|
||||
{
|
||||
_timer->cancel(shared_from_this());
|
||||
}
|
||||
|
||||
void BeaconEmitter::start()
|
||||
{
|
||||
_timer->scheduleAfterDelay(shared_from_this(), 0.0);
|
||||
}
|
||||
|
||||
void BeaconEmitter::reschedule()
|
||||
{
|
||||
const double period = (_beaconSequenceID >= _beaconCountLimit) ? _slowBeaconPeriod : _fastBeaconPeriod;
|
||||
if (period > 0)
|
||||
{
|
||||
_timer->scheduleAfterDelay(shared_from_this(), period);
|
||||
}
|
||||
}
|
||||
|
||||
void BeaconEmitter::callback()
|
||||
{
|
||||
_transport->enqueueSendRequest(shared_from_this());
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
153
src/server/beaconEmitter.h
Normal file
153
src/server/beaconEmitter.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BEACONEMITTER_H
|
||||
#define BEACONEMITTER_H
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define beaconEmitterEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <osiSock.h>
|
||||
|
||||
#include <pv/timer.h>
|
||||
#include <pv/timeStamp.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#ifdef beaconEmitterEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef beaconEmitterEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/remote.h>
|
||||
#include <pv/beaconServerStatusProvider.h>
|
||||
//#include <pv/serverContext.h>
|
||||
|
||||
namespace epics { namespace pvAccess {
|
||||
|
||||
class ServerContextImpl;
|
||||
|
||||
/**
|
||||
* BeaconEmitter
|
||||
*
|
||||
* @author gjansa
|
||||
*/
|
||||
class BeaconEmitter:
|
||||
public TransportSender,
|
||||
public epics::pvData::TimerCallback,
|
||||
public std::tr1::enable_shared_from_this<BeaconEmitter>
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<BeaconEmitter> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const BeaconEmitter> const_shared_pointer;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param transport transport to be used to send beacons.
|
||||
* @param context PVA context.
|
||||
*/
|
||||
// BeaconEmitter(Transport::shared_pointer const & transport, ServerContextImpl::shared_pointer const & context);
|
||||
BeaconEmitter(Transport::shared_pointer const & transport, std::tr1::shared_ptr<ServerContextImpl>& context);
|
||||
|
||||
/**
|
||||
* Test Constructor (ohne context)
|
||||
* @param transport transport to be used to send beacons.
|
||||
*/
|
||||
BeaconEmitter(Transport::shared_pointer const & transport, const osiSockAddr& serverAddress);
|
||||
|
||||
virtual ~BeaconEmitter();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
|
||||
void timerStopped();
|
||||
|
||||
/**
|
||||
* Start emitting.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Reschedule timer.
|
||||
*/
|
||||
void reschedule();
|
||||
|
||||
/**
|
||||
* Timer callback.
|
||||
*/
|
||||
void callback();
|
||||
|
||||
void destroy();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Minimal (initial) PVA beacon period (in seconds).
|
||||
*/
|
||||
static const float EPICS_PVA_MIN_BEACON_PERIOD;
|
||||
|
||||
/**
|
||||
* Minimal PVA beacon count limit.
|
||||
*/
|
||||
static const float EPICS_PVA_MIN_BEACON_COUNT_LIMIT;
|
||||
|
||||
/**
|
||||
* Transport.
|
||||
*/
|
||||
Transport::shared_pointer _transport;
|
||||
|
||||
/**
|
||||
* Beacon sequence ID.
|
||||
*/
|
||||
epics::pvData::int16 _beaconSequenceID;
|
||||
|
||||
/**
|
||||
* Startup timestamp (when clients detect a change, they will consider server restarted).
|
||||
*/
|
||||
epics::pvData::TimeStamp _startupTime;
|
||||
|
||||
/**
|
||||
* Fast (at startup) beacon period (in sec).
|
||||
*/
|
||||
double _fastBeaconPeriod;
|
||||
|
||||
/**
|
||||
* Slow (after beaconCountLimit is reached) beacon period (in sec).
|
||||
*/
|
||||
double _slowBeaconPeriod;
|
||||
|
||||
/**
|
||||
* Limit on number of beacons issued.
|
||||
*/
|
||||
epics::pvData::int16 _beaconCountLimit;
|
||||
|
||||
/**
|
||||
* Server address.
|
||||
*/
|
||||
const osiSockAddr _serverAddress;
|
||||
|
||||
/**
|
||||
* Server port.
|
||||
*/
|
||||
epics::pvData::int32 _serverPort;
|
||||
|
||||
/**
|
||||
* Server status provider implementation (optional).
|
||||
*/
|
||||
BeaconServerStatusProvider::shared_pointer _serverStatusProvider;
|
||||
|
||||
/**
|
||||
* Timer.
|
||||
*/
|
||||
epics::pvData::Timer::shared_pointer _timer;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* BEACONEMITTER_H */
|
||||
58
src/server/beaconServerStatusProvider.cpp
Normal file
58
src/server/beaconServerStatusProvider.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/beaconServerStatusProvider.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
DefaultBeaconServerStatusProvider::DefaultBeaconServerStatusProvider(ServerContext::shared_pointer const & context): _context(context)
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
DefaultBeaconServerStatusProvider::~DefaultBeaconServerStatusProvider()
|
||||
{
|
||||
}
|
||||
|
||||
void DefaultBeaconServerStatusProvider::initialize()
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
|
||||
StringArray fieldNames;
|
||||
fieldNames.resize(6);
|
||||
fieldNames[0] = "connections";
|
||||
fieldNames[1] = "allocatedMemory";
|
||||
fieldNames[2] = "freeMemory";
|
||||
fieldNames[3] = "threads";
|
||||
fieldNames[4] = "deadlocks";
|
||||
fieldNames[5] = "averageSystemLoad";
|
||||
|
||||
FieldConstPtrArray fields;
|
||||
fields.resize(6);
|
||||
// TODO hierarchy can be used...
|
||||
fields[0] = fieldCreate->createScalar(pvInt);
|
||||
fields[1] = fieldCreate->createScalar(pvLong);
|
||||
fields[2] = fieldCreate->createScalar(pvLong);
|
||||
fields[3] = fieldCreate->createScalar(pvInt);
|
||||
fields[4] = fieldCreate->createScalar(pvInt);
|
||||
fields[5] = fieldCreate->createScalar(pvDouble);
|
||||
|
||||
_status = getPVDataCreate()->createPVStructure(fieldCreate->createStructure(fieldNames, fields));
|
||||
}
|
||||
|
||||
PVField::shared_pointer DefaultBeaconServerStatusProvider::getServerStatusData()
|
||||
{
|
||||
//TODO implement (fill data)
|
||||
return _status;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
79
src/server/beaconServerStatusProvider.h
Normal file
79
src/server/beaconServerStatusProvider.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BEACONSERVERSTATUSPROVIDER_H
|
||||
#define BEACONSERVERSTATUSPROVIDER_H
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define beaconServerStatusProviderEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#ifdef beaconServerStatusProviderEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef beaconServerStatusProviderEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class ServerContext;
|
||||
|
||||
/**
|
||||
* BeaconServerStatusProvider
|
||||
*/
|
||||
class epicsShareClass BeaconServerStatusProvider
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<BeaconServerStatusProvider> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const BeaconServerStatusProvider> const_shared_pointer;
|
||||
|
||||
virtual ~BeaconServerStatusProvider() {};
|
||||
|
||||
/**
|
||||
* Gets server status data.
|
||||
*/
|
||||
virtual epics::pvData::PVField::shared_pointer getServerStatusData() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* DefaultBeaconServerStatusProvider
|
||||
*/
|
||||
class epicsShareClass DefaultBeaconServerStatusProvider : public BeaconServerStatusProvider
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param context PVA context.
|
||||
*/
|
||||
// DefaultBeaconServerStatusProvider(ServerContext::shared_pointer const & context);
|
||||
DefaultBeaconServerStatusProvider(std::tr1::shared_ptr<ServerContext> const & context);
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~DefaultBeaconServerStatusProvider();
|
||||
|
||||
virtual epics::pvData::PVField::shared_pointer getServerStatusData();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Initialize
|
||||
*/
|
||||
void initialize();
|
||||
|
||||
|
||||
private:
|
||||
epics::pvData::PVStructure::shared_pointer _status;
|
||||
std::tr1::shared_ptr<ServerContext> _context;
|
||||
//ServerContext::shared_pointer _context;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* BEACONSERVERSTATUSPROVIDER_H */
|
||||
2129
src/server/responseHandlers.cpp
Normal file
2129
src/server/responseHandlers.cpp
Normal file
File diff suppressed because it is too large
Load Diff
777
src/server/responseHandlers.h
Normal file
777
src/server/responseHandlers.h
Normal file
@@ -0,0 +1,777 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef RESPONSEHANDLERS_H_
|
||||
#define RESPONSEHANDLERS_H_
|
||||
|
||||
#include <pv/serverContext.h>
|
||||
#include <pv/remote.h>
|
||||
#include <pv/serverChannelImpl.h>
|
||||
#include <pv/baseChannelRequester.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
/**
|
||||
*/
|
||||
class AbstractServerResponseHandler : public AbstractResponseHandler {
|
||||
protected:
|
||||
ServerContextImpl::shared_pointer _context;
|
||||
public:
|
||||
AbstractServerResponseHandler(ServerContextImpl::shared_pointer const & context, epics::pvData::String description) :
|
||||
AbstractResponseHandler(context.get(), description), _context(context) {
|
||||
}
|
||||
|
||||
virtual ~AbstractServerResponseHandler() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Bad request handler.
|
||||
*/
|
||||
class ServerBadResponse : public AbstractServerResponseHandler {
|
||||
public:
|
||||
ServerBadResponse(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Bad request") {
|
||||
}
|
||||
|
||||
virtual ~ServerBadResponse() {
|
||||
}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
/**
|
||||
* PVAS request handler - main handler which dispatches requests to appropriate handlers.
|
||||
*/
|
||||
class ServerResponseHandler : public ResponseHandler {
|
||||
public:
|
||||
ServerResponseHandler(ServerContextImpl::shared_pointer const & context);
|
||||
|
||||
virtual ~ServerResponseHandler() {
|
||||
}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
private:
|
||||
/**
|
||||
* Table of response handlers for each command ID.
|
||||
*/
|
||||
std::vector<ResponseHandler::shared_pointer> m_handlerTable;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Connection validation message handler.
|
||||
*/
|
||||
class ServerConnectionValidationHandler : public AbstractServerResponseHandler {
|
||||
public:
|
||||
ServerConnectionValidationHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Connection validation") {
|
||||
}
|
||||
virtual ~ServerConnectionValidationHandler() {}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
/**
|
||||
* NOOP response.
|
||||
*/
|
||||
class ServerNoopResponse : public AbstractServerResponseHandler {
|
||||
public:
|
||||
ServerNoopResponse(ServerContextImpl::shared_pointer const & context, epics::pvData::String description) :
|
||||
AbstractServerResponseHandler(context, description) {
|
||||
}
|
||||
virtual ~ServerNoopResponse(){}
|
||||
};
|
||||
|
||||
/**
|
||||
* Echo request handler.
|
||||
*/
|
||||
class ServerEchoHandler : public AbstractServerResponseHandler {
|
||||
public:
|
||||
ServerEchoHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Echo request") {
|
||||
}
|
||||
virtual ~ServerEchoHandler(){}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
class EchoTransportSender : public TransportSender {
|
||||
public:
|
||||
EchoTransportSender(osiSockAddr* echoFrom) {
|
||||
memcpy(&_echoFrom, echoFrom, sizeof(osiSockAddr));
|
||||
}
|
||||
|
||||
virtual ~EchoTransportSender() {
|
||||
}
|
||||
|
||||
virtual void send(epics::pvData::ByteBuffer* /*buffer*/, TransportSendControl* control) {
|
||||
control->startMessage(CMD_ECHO, 0);
|
||||
control->setRecipient(_echoFrom);
|
||||
// TODO content
|
||||
}
|
||||
|
||||
virtual void lock() {
|
||||
}
|
||||
|
||||
virtual void unlock() {
|
||||
}
|
||||
|
||||
private:
|
||||
osiSockAddr _echoFrom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Introspection search request handler.
|
||||
*/
|
||||
class ServerIntrospectionSearchHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerIntrospectionSearchHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Search request") {
|
||||
}
|
||||
virtual ~ServerIntrospectionSearchHandler() {}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* Search channel request handler.
|
||||
*/
|
||||
// TODO object pool!!!
|
||||
class ServerSearchHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
// TODO
|
||||
static std::map<epics::pvData::String, std::tr1::weak_ptr<ChannelProvider> > s_channelNameToProvider;
|
||||
|
||||
ServerSearchHandler(ServerContextImpl::shared_pointer const & context);
|
||||
virtual ~ServerSearchHandler(){}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
|
||||
private:
|
||||
std::vector<ChannelProvider::shared_pointer> _providers;
|
||||
};
|
||||
|
||||
|
||||
class ServerChannelFindRequesterImpl:
|
||||
public ChannelFindRequester,
|
||||
public TransportSender,
|
||||
public std::tr1::enable_shared_from_this<ServerChannelFindRequesterImpl>
|
||||
{
|
||||
public:
|
||||
ServerChannelFindRequesterImpl(ServerContextImpl::shared_pointer const & context, epics::pvData::int32 expectedResponseCount);
|
||||
virtual ~ServerChannelFindRequesterImpl(){}
|
||||
void clear();
|
||||
ServerChannelFindRequesterImpl* set(epics::pvData::String _name, epics::pvData::int32 searchSequenceId, epics::pvData::int32 cid, osiSockAddr* sendTo, bool responseRequired);
|
||||
void channelFindResult(const epics::pvData::Status& status, ChannelFind::shared_pointer const & channelFind, bool wasFound);
|
||||
void lock();
|
||||
void unlock();
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
private:
|
||||
epics::pvData::String _name;
|
||||
epics::pvData::int32 _searchSequenceId;
|
||||
epics::pvData::int32 _cid;
|
||||
osiSockAddr* _sendTo;
|
||||
bool _responseRequired;
|
||||
bool _wasFound;
|
||||
ServerContextImpl::shared_pointer _context;
|
||||
epics::pvData::Mutex _mutex;
|
||||
epics::pvData::int32 _expectedResponseCount;
|
||||
epics::pvData::int32 _responseCount;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* Create channel request handler.
|
||||
*/
|
||||
class ServerCreateChannelHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerCreateChannelHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Create channel request") {
|
||||
_providers = context->getChannelProviders();
|
||||
}
|
||||
virtual ~ServerCreateChannelHandler() {}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
|
||||
private:
|
||||
void disconnect(Transport::shared_pointer const & transport);
|
||||
std::vector<ChannelProvider::shared_pointer> _providers;
|
||||
};
|
||||
|
||||
class ServerChannelRequesterImpl :
|
||||
public ChannelRequester,
|
||||
public TransportSender,
|
||||
public std::tr1::enable_shared_from_this<ServerChannelRequesterImpl>
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<ServerChannelRequesterImpl> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const ServerChannelRequesterImpl> const_shared_pointer;
|
||||
protected:
|
||||
ServerChannelRequesterImpl(Transport::shared_pointer const & transport, const epics::pvData::String channelName, const pvAccessID cid);
|
||||
public:
|
||||
virtual ~ServerChannelRequesterImpl() {}
|
||||
static ChannelRequester::shared_pointer create(ChannelProvider::shared_pointer const & provider, Transport::shared_pointer const & transport, const epics::pvData::String channelName, const pvAccessID cid);
|
||||
void channelCreated(const epics::pvData::Status& status, Channel::shared_pointer const & channel);
|
||||
void channelStateChange(Channel::shared_pointer const & c, const Channel::ConnectionState isConnected);
|
||||
epics::pvData::String getRequesterName();
|
||||
void message(epics::pvData::String const & message, epics::pvData::MessageType messageType);
|
||||
void lock();
|
||||
void unlock();
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
private:
|
||||
ServerChannel::weak_pointer _serverChannel;
|
||||
Transport::weak_pointer _transport;
|
||||
const epics::pvData::String _channelName;
|
||||
const pvAccessID _cid;
|
||||
epics::pvData::Status _status;
|
||||
epics::pvData::Mutex _mutex;
|
||||
void createChannelFailedResponse(epics::pvData::ByteBuffer* buffer, TransportSendControl* control, const epics::pvData::Status& status);
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* Destroy channel request handler.
|
||||
*/
|
||||
class ServerDestroyChannelHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerDestroyChannelHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Destroy channel request") {
|
||||
}
|
||||
virtual ~ServerDestroyChannelHandler(){}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
|
||||
class ServerDestroyChannelHandlerTransportSender : public TransportSender
|
||||
{
|
||||
public:
|
||||
ServerDestroyChannelHandlerTransportSender(pvAccessID cid, pvAccessID sid): _cid(cid), _sid(sid) {
|
||||
}
|
||||
|
||||
virtual ~ServerDestroyChannelHandlerTransportSender() {}
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control) {
|
||||
control->startMessage((epics::pvData::int8)CMD_DESTROY_CHANNEL, 2*sizeof(epics::pvData::int32)/sizeof(epics::pvData::int8));
|
||||
buffer->putInt(_sid);
|
||||
buffer->putInt(_cid);
|
||||
}
|
||||
|
||||
void lock() {
|
||||
// noop
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
// noop
|
||||
}
|
||||
|
||||
private:
|
||||
pvAccessID _cid;
|
||||
pvAccessID _sid;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* Get request handler.
|
||||
*/
|
||||
class ServerGetHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerGetHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Get request") {
|
||||
}
|
||||
virtual ~ServerGetHandler() {}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
class ServerChannelGetRequesterImpl :
|
||||
public BaseChannelRequester,
|
||||
public ChannelGetRequester,
|
||||
public TransportSender,
|
||||
public std::tr1::enable_shared_from_this<ServerChannelGetRequesterImpl>
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<ServerChannelGetRequesterImpl> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const ServerChannelGetRequesterImpl> const_shared_pointer;
|
||||
protected:
|
||||
ServerChannelGetRequesterImpl(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport);
|
||||
void activate(epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
public:
|
||||
static ChannelGetRequester::shared_pointer create(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual ~ServerChannelGetRequesterImpl() {}
|
||||
void channelGetConnect(const epics::pvData::Status& status, ChannelGet::shared_pointer const & channelGet,
|
||||
epics::pvData::PVStructure::shared_pointer const & pvStructure, epics::pvData::BitSet::shared_pointer const & bitSet);
|
||||
void getDone(const epics::pvData::Status& status);
|
||||
void destroy();
|
||||
|
||||
ChannelGet::shared_pointer getChannelGet();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
private:
|
||||
ChannelGet::shared_pointer _channelGet;
|
||||
epics::pvData::BitSet::shared_pointer _bitSet;
|
||||
epics::pvData::PVStructure::shared_pointer _pvStructure;
|
||||
epics::pvData::Status _status;
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* Put request handler.
|
||||
*/
|
||||
class ServerPutHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerPutHandler(ServerContextImpl::shared_pointer context) :
|
||||
AbstractServerResponseHandler(context, "Put request") {
|
||||
}
|
||||
virtual ~ServerPutHandler() {}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
class ServerChannelPutRequesterImpl :
|
||||
public BaseChannelRequester,
|
||||
public ChannelPutRequester,
|
||||
public TransportSender,
|
||||
public std::tr1::enable_shared_from_this<ServerChannelPutRequesterImpl>
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<ServerChannelPutRequesterImpl> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const ServerChannelPutRequesterImpl> const_shared_pointer;
|
||||
protected:
|
||||
ServerChannelPutRequesterImpl(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport);
|
||||
void activate(epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
public:
|
||||
static ChannelPutRequester::shared_pointer create(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport,epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
|
||||
virtual ~ServerChannelPutRequesterImpl() {}
|
||||
void channelPutConnect(const epics::pvData::Status& status, ChannelPut::shared_pointer const & channelPut, epics::pvData::PVStructure::shared_pointer const & pvStructure, epics::pvData::BitSet::shared_pointer const & bitSet);
|
||||
void putDone(const epics::pvData::Status& status);
|
||||
void getDone(const epics::pvData::Status& status);
|
||||
void lock();
|
||||
void unlock();
|
||||
void destroy();
|
||||
|
||||
ChannelPut::shared_pointer getChannelPut();
|
||||
epics::pvData::BitSet::shared_pointer getBitSet();
|
||||
epics::pvData::PVStructure::shared_pointer getPVStructure();
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
private:
|
||||
ChannelPut::shared_pointer _channelPut;
|
||||
epics::pvData::BitSet::shared_pointer _bitSet;
|
||||
epics::pvData::PVStructure::shared_pointer _pvStructure;
|
||||
epics::pvData::Status _status;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* Put request handler.
|
||||
*/
|
||||
class ServerPutGetHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerPutGetHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Put-get request") {
|
||||
}
|
||||
|
||||
virtual ~ServerPutGetHandler() {}
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
class ServerChannelPutGetRequesterImpl :
|
||||
public BaseChannelRequester,
|
||||
public ChannelPutGetRequester,
|
||||
public TransportSender,
|
||||
public std::tr1::enable_shared_from_this<ServerChannelPutGetRequesterImpl>
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<ServerChannelPutGetRequesterImpl> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const ServerChannelPutGetRequesterImpl> const_shared_pointer;
|
||||
protected:
|
||||
ServerChannelPutGetRequesterImpl(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport);
|
||||
void activate(epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
public:
|
||||
static ChannelPutGetRequester::shared_pointer create(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport,epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual ~ServerChannelPutGetRequesterImpl() {}
|
||||
|
||||
void channelPutGetConnect(const epics::pvData::Status& status, ChannelPutGet::shared_pointer const & channelPutGet, epics::pvData::PVStructure::shared_pointer const & pvPutStructure, epics::pvData::PVStructure::shared_pointer const & pvGetStructure);
|
||||
void getGetDone(const epics::pvData::Status& status);
|
||||
void getPutDone(const epics::pvData::Status& status);
|
||||
void putGetDone(const epics::pvData::Status& status);
|
||||
void lock();
|
||||
void unlock();
|
||||
void destroy();
|
||||
|
||||
ChannelPutGet::shared_pointer getChannelPutGet();
|
||||
epics::pvData::PVStructure::shared_pointer getPVPutStructure();
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
private:
|
||||
ChannelPutGet::shared_pointer _channelPutGet;
|
||||
epics::pvData::PVStructure::shared_pointer _pvPutStructure;
|
||||
epics::pvData::PVStructure::shared_pointer _pvGetStructure;
|
||||
epics::pvData::Status _status;
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* Monitor request handler.
|
||||
*/
|
||||
class ServerMonitorHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerMonitorHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Monitor request") {
|
||||
}
|
||||
virtual ~ServerMonitorHandler() {}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
|
||||
class ServerMonitorRequesterImpl :
|
||||
public BaseChannelRequester,
|
||||
public epics::pvData::MonitorRequester,
|
||||
public TransportSender,
|
||||
public std::tr1::enable_shared_from_this<ServerMonitorRequesterImpl>
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<ServerMonitorRequesterImpl> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const ServerMonitorRequesterImpl> const_shared_pointer;
|
||||
protected:
|
||||
ServerMonitorRequesterImpl(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport);
|
||||
void activate(epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
public:
|
||||
static epics::pvData::MonitorRequester::shared_pointer create(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport,epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual ~ServerMonitorRequesterImpl(){}
|
||||
|
||||
void monitorConnect(const epics::pvData::Status& status, epics::pvData::Monitor::shared_pointer const & monitor, epics::pvData::StructureConstPtr const & structure);
|
||||
void unlisten(epics::pvData::Monitor::shared_pointer const & monitor);
|
||||
void monitorEvent(epics::pvData::Monitor::shared_pointer const & monitor);
|
||||
void lock();
|
||||
void unlock();
|
||||
void destroy();
|
||||
|
||||
epics::pvData::Monitor::shared_pointer getChannelMonitor();
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
private:
|
||||
epics::pvData::Monitor::shared_pointer _channelMonitor;
|
||||
epics::pvData::StructureConstPtr _structure;
|
||||
epics::pvData::Status _status;
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* Array request handler.
|
||||
*/
|
||||
class ServerArrayHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerArrayHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Array request") {
|
||||
}
|
||||
virtual ~ServerArrayHandler(){}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
class ServerChannelArrayRequesterImpl :
|
||||
public BaseChannelRequester,
|
||||
public ChannelArrayRequester,
|
||||
public TransportSender,
|
||||
public std::tr1::enable_shared_from_this<ServerChannelArrayRequesterImpl>
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<ServerChannelArrayRequesterImpl> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const ServerChannelArrayRequesterImpl> const_shared_pointer;
|
||||
protected:
|
||||
ServerChannelArrayRequesterImpl(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport);
|
||||
void activate(epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
public:
|
||||
static ChannelArrayRequester::shared_pointer create(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport,epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual ~ServerChannelArrayRequesterImpl() {}
|
||||
|
||||
void channelArrayConnect(const epics::pvData::Status& status, ChannelArray::shared_pointer const & channelArray, epics::pvData::PVArray::shared_pointer const & pvArray);
|
||||
void getArrayDone(const epics::pvData::Status& status);
|
||||
void putArrayDone(const epics::pvData::Status& status);
|
||||
void setLengthDone(const epics::pvData::Status& status);
|
||||
void lock();
|
||||
void unlock();
|
||||
void destroy();
|
||||
|
||||
ChannelArray::shared_pointer getChannelArray();
|
||||
|
||||
epics::pvData::PVArray::shared_pointer getPVArray();
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
|
||||
private:
|
||||
ChannelArray::shared_pointer _channelArray;
|
||||
epics::pvData::PVArray::shared_pointer _pvArray;
|
||||
epics::pvData::Status _status;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* Cancel request handler.
|
||||
*/
|
||||
class ServerCancelRequestHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerCancelRequestHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Cancel request") {
|
||||
}
|
||||
virtual ~ServerCancelRequestHandler() {}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
private:
|
||||
|
||||
void failureResponse(Transport::shared_pointer const & transport, pvAccessID ioid, const epics::pvData::Status& errorStatus);
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* Process request handler.
|
||||
*/
|
||||
class ServerProcessHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerProcessHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Process request") {
|
||||
}
|
||||
virtual ~ServerProcessHandler() {}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
class ServerChannelProcessRequesterImpl :
|
||||
public BaseChannelRequester,
|
||||
public ChannelProcessRequester,
|
||||
public TransportSender,
|
||||
public std::tr1::enable_shared_from_this<ServerChannelProcessRequesterImpl>
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<ServerChannelProcessRequesterImpl> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const ServerChannelProcessRequesterImpl> const_shared_pointer;
|
||||
protected:
|
||||
ServerChannelProcessRequesterImpl(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport);
|
||||
void activate(epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
public:
|
||||
static ChannelProcessRequester::shared_pointer create(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport, epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual ~ServerChannelProcessRequesterImpl() {}
|
||||
|
||||
void channelProcessConnect(const epics::pvData::Status& status, ChannelProcess::shared_pointer const & channelProcess);
|
||||
void processDone(const epics::pvData::Status& status);
|
||||
void lock();
|
||||
void unlock();
|
||||
void destroy();
|
||||
|
||||
ChannelProcess::shared_pointer getChannelProcess();
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
|
||||
private:
|
||||
ChannelProcess::shared_pointer _channelProcess;
|
||||
epics::pvData::Status _status;
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* Get field request handler.
|
||||
*/
|
||||
class ServerGetFieldHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerGetFieldHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "Get field request") {
|
||||
}
|
||||
virtual ~ServerGetFieldHandler() {}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
private:
|
||||
void getFieldFailureResponse(Transport::shared_pointer const & transport, const pvAccessID ioid, const epics::pvData::Status& errorStatus);
|
||||
};
|
||||
|
||||
class ServerGetFieldRequesterImpl :
|
||||
public BaseChannelRequester,
|
||||
public GetFieldRequester,
|
||||
public TransportSender,
|
||||
public std::tr1::enable_shared_from_this<ServerGetFieldRequesterImpl>
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<ServerGetFieldRequesterImpl> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const ServerGetFieldRequesterImpl> const_shared_pointer;
|
||||
|
||||
ServerGetFieldRequesterImpl(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport);
|
||||
|
||||
virtual ~ServerGetFieldRequesterImpl() {}
|
||||
void getDone(const epics::pvData::Status& status, epics::pvData::FieldConstPtr const & field);
|
||||
void lock();
|
||||
void unlock();
|
||||
void destroy();
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
private:
|
||||
epics::pvData::Status _status;
|
||||
epics::pvData::FieldConstPtr _field;
|
||||
};
|
||||
|
||||
class ServerGetFieldHandlerTransportSender : public TransportSender
|
||||
{
|
||||
public:
|
||||
ServerGetFieldHandlerTransportSender(const pvAccessID ioid,const epics::pvData::Status& status, Transport::shared_pointer const & transport):
|
||||
_ioid(ioid), _status(status), _transport(transport) {
|
||||
|
||||
}
|
||||
virtual ~ServerGetFieldHandlerTransportSender() {}
|
||||
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control) {
|
||||
control->startMessage((epics::pvData::int8)CMD_GET_FIELD, sizeof(epics::pvData::int32)/sizeof(epics::pvData::int8));
|
||||
buffer->putInt(_ioid);
|
||||
_status.serialize(buffer, control);
|
||||
}
|
||||
|
||||
void lock() {
|
||||
// noop
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
// noop
|
||||
}
|
||||
|
||||
private:
|
||||
const pvAccessID _ioid;
|
||||
const epics::pvData::Status _status;
|
||||
Transport::shared_pointer const & _transport;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/****************************************************************************************/
|
||||
/**
|
||||
* RPC handler.
|
||||
*/
|
||||
class ServerRPCHandler : public AbstractServerResponseHandler
|
||||
{
|
||||
public:
|
||||
ServerRPCHandler(ServerContextImpl::shared_pointer const & context) :
|
||||
AbstractServerResponseHandler(context, "RPC request") {
|
||||
}
|
||||
virtual ~ServerRPCHandler() {}
|
||||
|
||||
virtual void handleResponse(osiSockAddr* responseFrom,
|
||||
Transport::shared_pointer const & transport, epics::pvData::int8 version, epics::pvData::int8 command,
|
||||
std::size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer);
|
||||
};
|
||||
|
||||
class ServerChannelRPCRequesterImpl :
|
||||
public BaseChannelRequester,
|
||||
public ChannelRPCRequester,
|
||||
public TransportSender,
|
||||
public std::tr1::enable_shared_from_this<ServerChannelRPCRequesterImpl>
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<ServerChannelRPCRequesterImpl> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const ServerChannelRPCRequesterImpl> const_shared_pointer;
|
||||
protected:
|
||||
ServerChannelRPCRequesterImpl(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport);
|
||||
void activate(epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
public:
|
||||
static ChannelRPCRequester::shared_pointer create(ServerContextImpl::shared_pointer const & context,
|
||||
ServerChannelImpl::shared_pointer const & channel, const pvAccessID ioid,
|
||||
Transport::shared_pointer const & transport,epics::pvData::PVStructure::shared_pointer const & pvRequest);
|
||||
virtual ~ServerChannelRPCRequesterImpl() {}
|
||||
|
||||
void channelRPCConnect(const epics::pvData::Status& status, ChannelRPC::shared_pointer const & channelRPC);
|
||||
void requestDone(const epics::pvData::Status& status, epics::pvData::PVStructure::shared_pointer const & pvResponse);
|
||||
void lock();
|
||||
void unlock();
|
||||
void destroy();
|
||||
/**
|
||||
* @return the channelRPC
|
||||
*/
|
||||
ChannelRPC::shared_pointer getChannelRPC();
|
||||
|
||||
void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control);
|
||||
private:
|
||||
ChannelRPC::shared_pointer _channelRPC;
|
||||
epics::pvData::PVStructure::shared_pointer _pvResponse;
|
||||
epics::pvData::Status _status;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* RESPONSEHANDLERS_H_ */
|
||||
121
src/server/serverChannelImpl.cpp
Normal file
121
src/server/serverChannelImpl.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* 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/serverChannelImpl.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
ServerChannelImpl::ServerChannelImpl(Channel::shared_pointer const & channel, pvAccessID cid, pvAccessID sid, epics::pvData::PVField::shared_pointer const & /*securityToken*/):
|
||||
_channel(channel),
|
||||
_cid(cid),
|
||||
_sid(sid),
|
||||
_destroyed(false)
|
||||
{
|
||||
if (!channel.get())
|
||||
{
|
||||
THROW_BASE_EXCEPTION("non-null channel required");
|
||||
}
|
||||
}
|
||||
|
||||
Channel::shared_pointer ServerChannelImpl::getChannel()
|
||||
{
|
||||
return _channel;
|
||||
}
|
||||
|
||||
pvAccessID ServerChannelImpl::getCID() const
|
||||
{
|
||||
return _cid;
|
||||
}
|
||||
|
||||
pvAccessID ServerChannelImpl::getSID() const
|
||||
{
|
||||
return _sid;
|
||||
}
|
||||
|
||||
int16 ServerChannelImpl::getAccessRights()
|
||||
{
|
||||
//TODO implement
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ServerChannelImpl::registerRequest(const pvAccessID id, Destroyable::shared_pointer const & request)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
_requests[id] = request;
|
||||
}
|
||||
|
||||
void ServerChannelImpl::unregisterRequest(const pvAccessID id)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
std::map<pvAccessID, epics::pvData::Destroyable::shared_pointer>::iterator iter = _requests.find(id);
|
||||
if(iter != _requests.end())
|
||||
{
|
||||
_requests.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
Destroyable::shared_pointer ServerChannelImpl::getRequest(const pvAccessID id)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
std::map<pvAccessID, epics::pvData::Destroyable::shared_pointer>::iterator iter = _requests.find(id);
|
||||
if(iter != _requests.end())
|
||||
{
|
||||
return iter->second;
|
||||
}
|
||||
return Destroyable::shared_pointer();
|
||||
}
|
||||
|
||||
void ServerChannelImpl::destroy()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
if (_destroyed) return;
|
||||
_destroyed = true;
|
||||
|
||||
// destroy all requests
|
||||
destroyAllRequests();
|
||||
|
||||
// ... and the channel
|
||||
// TODO try catch
|
||||
_channel->destroy();
|
||||
}
|
||||
|
||||
ServerChannelImpl::~ServerChannelImpl()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void ServerChannelImpl::printInfo()
|
||||
{
|
||||
printInfo(stdout);
|
||||
}
|
||||
|
||||
void ServerChannelImpl::printInfo(FILE *fd)
|
||||
{
|
||||
fprintf(fd,"CLASS : %s\n", typeid(*this).name());
|
||||
fprintf(fd,"CHANNEL : %s\n", typeid(*_channel).name());
|
||||
}
|
||||
|
||||
void ServerChannelImpl::destroyAllRequests()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
|
||||
// resource allocation optimization
|
||||
if (_requests.size() == 0)
|
||||
return;
|
||||
|
||||
while(_requests.size() != 0)
|
||||
{
|
||||
_requests.begin()->second->destroy();
|
||||
}
|
||||
|
||||
_requests.clear();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
149
src/server/serverChannelImpl.h
Normal file
149
src/server/serverChannelImpl.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SERVERCHANNEL_H_
|
||||
#define SERVERCHANNEL_H_
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define serverChannelImplEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/destroyable.h>
|
||||
|
||||
#ifdef serverChannelImplEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef serverChannelImplEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/remote.h>
|
||||
#include <pv/clientContextImpl.h>
|
||||
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class ServerChannelImpl : public ServerChannel
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ServerChannelImpl);
|
||||
|
||||
/**
|
||||
* Create server channel for given process variable.
|
||||
* @param channel local channel.
|
||||
* @param cid channel CID.
|
||||
* @param sid channel SID.
|
||||
* @param securityToken security token.
|
||||
*/
|
||||
ServerChannelImpl(Channel::shared_pointer const & channel, pvAccessID cid, pvAccessID sid, epics::pvData::PVField::shared_pointer const & securityToken);
|
||||
/*
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~ServerChannelImpl();
|
||||
|
||||
/**
|
||||
* Get local channel.
|
||||
* @return local channel.
|
||||
*/
|
||||
Channel::shared_pointer getChannel();
|
||||
|
||||
/**
|
||||
* Get channel CID.
|
||||
* @return channel CID.
|
||||
*/
|
||||
pvAccessID getCID() const;
|
||||
|
||||
/**
|
||||
* Get channel SID.
|
||||
* @return channel SID.
|
||||
*/
|
||||
pvAccessID getSID() const;
|
||||
|
||||
/**
|
||||
* Get access rights (bit-mask encoded).
|
||||
* @see AccessRights
|
||||
* @return bit-mask encoded access rights.
|
||||
*/
|
||||
epics::pvData::int16 getAccessRights();
|
||||
|
||||
/**
|
||||
* Register request
|
||||
* @param id request ID.
|
||||
* @param request request to be registered.
|
||||
*/
|
||||
void registerRequest(pvAccessID id, epics::pvData::Destroyable::shared_pointer const & request);
|
||||
|
||||
/**
|
||||
* Unregister request.
|
||||
* @param id request ID.
|
||||
*/
|
||||
void unregisterRequest(pvAccessID id);
|
||||
|
||||
/**
|
||||
* Get request by its ID.
|
||||
* @param id request ID.
|
||||
* @return request with given ID, <code>null</code> if there is no request with such ID.
|
||||
*/
|
||||
epics::pvData::Destroyable::shared_pointer getRequest(pvAccessID id);
|
||||
|
||||
/**
|
||||
* Destroy server channel.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Prints detailed information about the process variable to the standard output stream.
|
||||
*/
|
||||
void printInfo();
|
||||
|
||||
/**
|
||||
* Prints detailed information about the process variable to the specified output
|
||||
* stream.
|
||||
* @param fd the output stream.
|
||||
*/
|
||||
void printInfo(FILE *fd);
|
||||
private:
|
||||
/**
|
||||
* Local channel.
|
||||
*/
|
||||
Channel::shared_pointer _channel;
|
||||
|
||||
/**
|
||||
* Channel CID.
|
||||
*/
|
||||
pvAccessID _cid;
|
||||
|
||||
/**
|
||||
* Channel SID.
|
||||
*/
|
||||
pvAccessID _sid;
|
||||
|
||||
/**
|
||||
* Requests.
|
||||
*/
|
||||
std::map<pvAccessID, epics::pvData::Destroyable::shared_pointer> _requests;
|
||||
|
||||
/**
|
||||
* Destroy state.
|
||||
*/
|
||||
bool _destroyed;
|
||||
|
||||
/**
|
||||
* Mutex
|
||||
*/
|
||||
epics::pvData::Mutex _mutex;
|
||||
|
||||
/**
|
||||
* Destroy all registered requests.
|
||||
*/
|
||||
void destroyAllRequests();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* SERVERCHANNEL_H_ */
|
||||
655
src/server/serverContext.cpp
Normal file
655
src/server/serverContext.cpp
Normal file
@@ -0,0 +1,655 @@
|
||||
/**
|
||||
* 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 <epicsSignal.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/responseHandlers.h>
|
||||
#include <pv/logger.h>
|
||||
#include <pv/serverContext.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace epics::pvData;
|
||||
using std::tr1::dynamic_pointer_cast;
|
||||
using std::tr1::static_pointer_cast;
|
||||
|
||||
namespace epics { namespace pvAccess {
|
||||
|
||||
const char* ServerContextImpl::StateNames[] = { "NOT_INITIALIZED", "INITIALIZED", "RUNNING", "SHUTDOWN", "DESTROYED"};
|
||||
const Version ServerContextImpl::VERSION("pvAccess Server", "cpp",
|
||||
EPICS_PVA_MAJOR_VERSION, EPICS_PVA_MINOR_VERSION, EPICS_PVA_MAINTENANCE_VERSION, EPICS_PVA_DEVELOPMENT_FLAG);
|
||||
|
||||
ServerContextImpl::ServerContextImpl():
|
||||
_state(NOT_INITIALIZED),
|
||||
_beaconAddressList(),
|
||||
_ignoreAddressList(),
|
||||
_autoBeaconAddressList(true),
|
||||
_beaconPeriod(15.0),
|
||||
_broadcastPort(PVA_BROADCAST_PORT),
|
||||
_serverPort(PVA_SERVER_PORT),
|
||||
_receiveBufferSize(MAX_TCP_RECV),
|
||||
_timer(),
|
||||
_broadcastTransport(),
|
||||
_beaconEmitter(),
|
||||
_acceptor(),
|
||||
_transportRegistry(),
|
||||
_channelAccess(),
|
||||
_channelProviderNames(PVACCESS_DEFAULT_PROVIDER),
|
||||
_channelProviders(),
|
||||
_beaconServerStatusProvider()
|
||||
|
||||
{
|
||||
// TODO maybe there is a better place for this (when there will be some factory)
|
||||
epicsSignalInstallSigAlarmIgnore ();
|
||||
epicsSignalInstallSigPipeIgnore ();
|
||||
|
||||
initializeLogger();
|
||||
loadConfiguration();
|
||||
}
|
||||
|
||||
ServerContextImpl::shared_pointer ServerContextImpl::create()
|
||||
{
|
||||
ServerContextImpl::shared_pointer thisPointer(new ServerContextImpl());
|
||||
return thisPointer;
|
||||
}
|
||||
|
||||
ServerContextImpl::~ServerContextImpl()
|
||||
{
|
||||
dispose();
|
||||
}
|
||||
|
||||
const Version& ServerContextImpl::getVersion()
|
||||
{
|
||||
return ServerContextImpl::VERSION;
|
||||
}
|
||||
|
||||
void ServerContextImpl::initializeLogger()
|
||||
{
|
||||
//createFileLogger("serverContextImpl.log");
|
||||
}
|
||||
|
||||
struct noop_deleter
|
||||
{
|
||||
template<class T> void operator()(T * p) {}
|
||||
};
|
||||
|
||||
Configuration::shared_pointer ServerContextImpl::getConfiguration()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
if (configuration.get() == 0)
|
||||
{
|
||||
ConfigurationProvider::shared_pointer configurationProvider = ConfigurationFactory::getProvider();
|
||||
configuration = configurationProvider->getConfiguration("pvAccess-server");
|
||||
if (configuration.get() == 0)
|
||||
{
|
||||
configuration = configurationProvider->getConfiguration("system");
|
||||
}
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration.
|
||||
*/
|
||||
void ServerContextImpl::loadConfiguration()
|
||||
{
|
||||
Configuration::shared_pointer config = getConfiguration();
|
||||
|
||||
_beaconAddressList = config->getPropertyAsString("EPICS_PVA_ADDR_LIST", _beaconAddressList);
|
||||
_beaconAddressList = config->getPropertyAsString("EPICS_PVAS_BEACON_ADDR_LIST", _beaconAddressList);
|
||||
|
||||
_autoBeaconAddressList = config->getPropertyAsBoolean("EPICS_PVA_AUTO_ADDR_LIST", _autoBeaconAddressList);
|
||||
_autoBeaconAddressList = config->getPropertyAsBoolean("EPICS_PVAS_AUTO_BEACON_ADDR_LIST", _autoBeaconAddressList);
|
||||
|
||||
_beaconPeriod = config->getPropertyAsFloat("EPICS_PVA_BEACON_PERIOD", _beaconPeriod);
|
||||
_beaconPeriod = config->getPropertyAsFloat("EPICS_PVAS_BEACON_PERIOD", _beaconPeriod);
|
||||
|
||||
_serverPort = config->getPropertyAsInteger("EPICS_PVA_SERVER_PORT", _serverPort);
|
||||
_serverPort = config->getPropertyAsInteger("EPICS_PVAS_SERVER_PORT", _serverPort);
|
||||
|
||||
_broadcastPort = config->getPropertyAsInteger("EPICS_PVA_BROADCAST_PORT", _broadcastPort);
|
||||
_broadcastPort = config->getPropertyAsInteger("EPICS_PVAS_BROADCAST_PORT", _broadcastPort);
|
||||
|
||||
_receiveBufferSize = config->getPropertyAsInteger("EPICS_PVA_MAX_ARRAY_BYTES", _receiveBufferSize);
|
||||
_receiveBufferSize = config->getPropertyAsInteger("EPICS_PVAS_MAX_ARRAY_BYTES", _receiveBufferSize);
|
||||
|
||||
_channelProviderNames = config->getPropertyAsString("EPICS_PVA_PROVIDER_NAMES", _channelProviderNames);
|
||||
_channelProviderNames = config->getPropertyAsString("EPICS_PVAS_PROVIDER_NAMES", _channelProviderNames);
|
||||
}
|
||||
|
||||
bool ServerContextImpl::isChannelProviderNamePreconfigured()
|
||||
{
|
||||
Configuration::shared_pointer config = getConfiguration();
|
||||
return config->hasProperty("EPICS_PVA_PROVIDER_NAMES") || config->hasProperty("EPICS_PVAS_PROVIDER_NAMES");
|
||||
}
|
||||
|
||||
void ServerContextImpl::initialize(ChannelAccess::shared_pointer const & channelAccess)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
if (channelAccess == NULL)
|
||||
{
|
||||
THROW_BASE_EXCEPTION("non null channelAccess expected");
|
||||
}
|
||||
|
||||
if (_state == DESTROYED)
|
||||
{
|
||||
THROW_BASE_EXCEPTION("Context destroyed.");
|
||||
}
|
||||
else if (_state != NOT_INITIALIZED)
|
||||
{
|
||||
THROW_BASE_EXCEPTION("Context already initialized.");
|
||||
}
|
||||
|
||||
_channelAccess = channelAccess;
|
||||
|
||||
|
||||
// user all providers
|
||||
if (_channelProviderNames == PVACCESS_ALL_PROVIDERS)
|
||||
{
|
||||
_channelProviderNames.resize(0); // VxWorks 5.5 omits clear()
|
||||
|
||||
std::auto_ptr<ChannelAccess::stringVector_t> names = _channelAccess->getProviderNames();
|
||||
for (ChannelAccess::stringVector_t::iterator iter = names->begin(); iter != names->end(); iter++)
|
||||
{
|
||||
ChannelProvider::shared_pointer channelProvider = _channelAccess->getProvider(*iter);
|
||||
if (channelProvider)
|
||||
{
|
||||
_channelProviders.push_back(channelProvider);
|
||||
|
||||
// compile a list
|
||||
if (!_channelProviderNames.empty())
|
||||
_channelProviderNames += ' ';
|
||||
_channelProviderNames += *iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// split space separated names
|
||||
std::stringstream ss(_channelProviderNames);
|
||||
std::string providerName;
|
||||
while (std::getline(ss, providerName, ' '))
|
||||
{
|
||||
ChannelProvider::shared_pointer channelProvider = _channelAccess->getProvider(providerName);
|
||||
if (channelProvider)
|
||||
_channelProviders.push_back(channelProvider);
|
||||
}
|
||||
}
|
||||
|
||||
//_channelProvider = _channelAccess->getProvider(_channelProviderNames);
|
||||
if (_channelProviders.size() == 0)
|
||||
{
|
||||
std::string msg = "None of the specified channel providers are available: " + _channelProviderNames + ".";
|
||||
THROW_BASE_EXCEPTION(msg.c_str());
|
||||
}
|
||||
|
||||
internalInitialize();
|
||||
|
||||
_state = INITIALIZED;
|
||||
}
|
||||
|
||||
std::auto_ptr<ResponseHandler> ServerContextImpl::createResponseHandler()
|
||||
{
|
||||
ServerContextImpl::shared_pointer thisContext = shared_from_this();
|
||||
return std::auto_ptr<ResponseHandler>(new ServerResponseHandler(thisContext));
|
||||
}
|
||||
|
||||
void ServerContextImpl::internalInitialize()
|
||||
{
|
||||
osiSockAttach();
|
||||
|
||||
_timer.reset(new Timer("pvAccess-server timer", lowerPriority));
|
||||
_transportRegistry.reset(new TransportRegistry());
|
||||
|
||||
ServerContextImpl::shared_pointer thisServerContext = shared_from_this();
|
||||
|
||||
_acceptor.reset(new BlockingTCPAcceptor(thisServerContext, thisServerContext, _serverPort, _receiveBufferSize));
|
||||
_serverPort = ntohs(_acceptor->getBindAddress()->ia.sin_port);
|
||||
|
||||
// setup broadcast UDP transport
|
||||
initializeBroadcastTransport();
|
||||
|
||||
_beaconEmitter.reset(new BeaconEmitter(_broadcastTransport, thisServerContext));
|
||||
}
|
||||
|
||||
void ServerContextImpl::initializeBroadcastTransport()
|
||||
{
|
||||
// setup UDP transport
|
||||
try
|
||||
{
|
||||
// where to bind (listen) address
|
||||
osiSockAddr listenLocalAddress;
|
||||
listenLocalAddress.ia.sin_family = AF_INET;
|
||||
listenLocalAddress.ia.sin_port = htons(_broadcastPort);
|
||||
listenLocalAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
// where to send addresses
|
||||
SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (socket == INVALID_SOCKET)
|
||||
{
|
||||
THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport");
|
||||
}
|
||||
auto_ptr<InetAddrVector> broadcastAddresses(getBroadcastAddresses(socket,_broadcastPort));
|
||||
epicsSocketDestroy(socket);
|
||||
|
||||
TransportClient::shared_pointer nullTransportClient;
|
||||
|
||||
auto_ptr<BlockingUDPConnector> broadcastConnector(new BlockingUDPConnector(true, true));
|
||||
auto_ptr<epics::pvAccess::ResponseHandler> responseHandler = createResponseHandler();
|
||||
_broadcastTransport = static_pointer_cast<BlockingUDPTransport>(broadcastConnector->connect(
|
||||
nullTransportClient, responseHandler,
|
||||
listenLocalAddress, PVA_PROTOCOL_REVISION,
|
||||
PVA_DEFAULT_PRIORITY));
|
||||
_broadcastTransport->setBroadcastAddresses(broadcastAddresses.get());
|
||||
|
||||
// set ignore address list
|
||||
if (!_ignoreAddressList.empty())
|
||||
{
|
||||
// we do not care about the port
|
||||
auto_ptr<InetAddrVector> list(getSocketAddressList(_ignoreAddressList, 0, NULL));
|
||||
if (list.get() != NULL && list->size() > 0)
|
||||
{
|
||||
_broadcastTransport->setIgnoredAddresses(list.get());
|
||||
}
|
||||
}
|
||||
// set broadcast address list
|
||||
if (!_beaconAddressList.empty())
|
||||
{
|
||||
// if auto is true, add it to specified list
|
||||
InetAddrVector* appendList = NULL;
|
||||
if (_autoBeaconAddressList == true)
|
||||
{
|
||||
appendList = _broadcastTransport->getSendAddresses();
|
||||
}
|
||||
|
||||
auto_ptr<InetAddrVector> list(getSocketAddressList(_beaconAddressList, _broadcastPort, appendList));
|
||||
if (list.get() != NULL && list->size() > 0)
|
||||
{
|
||||
_broadcastTransport->setBroadcastAddresses(list.get());
|
||||
}
|
||||
}
|
||||
|
||||
_broadcastTransport->start();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
THROW_BASE_EXCEPTION_CAUSE("Failed to initialize broadcast UDP transport", e);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport");
|
||||
}
|
||||
}
|
||||
|
||||
void ServerContextImpl::run(int32 seconds)
|
||||
{
|
||||
if (seconds < 0)
|
||||
{
|
||||
THROW_BASE_EXCEPTION("seconds cannot be negative.");
|
||||
}
|
||||
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
|
||||
if (_state == NOT_INITIALIZED)
|
||||
{
|
||||
THROW_BASE_EXCEPTION("Context not initialized.");
|
||||
}
|
||||
else if (_state == DESTROYED)
|
||||
{
|
||||
THROW_BASE_EXCEPTION("Context destroyed.");
|
||||
}
|
||||
else if (_state == RUNNING)
|
||||
{
|
||||
THROW_BASE_EXCEPTION("Context is already running.");
|
||||
}
|
||||
else if (_state == SHUTDOWN)
|
||||
{
|
||||
THROW_BASE_EXCEPTION("Context was shutdown.");
|
||||
}
|
||||
|
||||
_state = RUNNING;
|
||||
}
|
||||
|
||||
// run...
|
||||
_beaconEmitter->start();
|
||||
|
||||
//TODO review this
|
||||
if(seconds == 0)
|
||||
{
|
||||
_runEvent.wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
_runEvent.wait(seconds);
|
||||
}
|
||||
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
_state = SHUTDOWN;
|
||||
}
|
||||
}
|
||||
|
||||
void ServerContextImpl::shutdown()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
if(_state == DESTROYED)
|
||||
{
|
||||
THROW_BASE_EXCEPTION("Context already destroyed.");
|
||||
}
|
||||
|
||||
// notify to stop running...
|
||||
_runEvent.signal();
|
||||
}
|
||||
|
||||
void ServerContextImpl::destroy()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
if (_state == DESTROYED)
|
||||
{
|
||||
// silent return
|
||||
return;
|
||||
// exception is not OK, since we use
|
||||
// shared_pointer-s auto-cleanup/destruction
|
||||
// THROW_BASE_EXCEPTION("Context already destroyed.");
|
||||
}
|
||||
|
||||
// shutdown if not already
|
||||
shutdown();
|
||||
|
||||
// go into destroyed state ASAP
|
||||
_state = DESTROYED;
|
||||
|
||||
internalDestroy();
|
||||
}
|
||||
|
||||
|
||||
void ServerContextImpl::internalDestroy()
|
||||
{
|
||||
// stop responding to search requests
|
||||
if (_broadcastTransport != NULL)
|
||||
{
|
||||
_broadcastTransport->close();
|
||||
_broadcastTransport.reset();
|
||||
}
|
||||
|
||||
// stop accepting connections
|
||||
if (_acceptor != NULL)
|
||||
{
|
||||
_acceptor->destroy();
|
||||
_acceptor.reset();
|
||||
}
|
||||
|
||||
// stop emitting beacons
|
||||
if (_beaconEmitter != NULL)
|
||||
{
|
||||
_beaconEmitter->destroy();
|
||||
_beaconEmitter.reset();
|
||||
}
|
||||
|
||||
// this will also destroy all channels
|
||||
destroyAllTransports();
|
||||
}
|
||||
|
||||
void ServerContextImpl::destroyAllTransports()
|
||||
{
|
||||
|
||||
// not initialized yet
|
||||
if (_transportRegistry == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::auto_ptr<TransportRegistry::transportVector_t> transports = _transportRegistry->toArray();
|
||||
if (transports.get() == 0)
|
||||
return;
|
||||
|
||||
int size = (int)transports->size();
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
LOG(logLevelInfo, "Server context still has %d transport(s) active and closing...", size);
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
Transport::shared_pointer transport = (*transports)[i];
|
||||
try
|
||||
{
|
||||
transport->close();
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
// do all exception safe, log in case of an error
|
||||
LOG(logLevelError, "Unhandled exception caught from client code at %s:%d: %s", __FILE__, __LINE__, e.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// do all exception safe, log in case of an error
|
||||
LOG(logLevelError, "Unhandled exception caught from client code at %s:%d.", __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
// now clear all (release)
|
||||
_transportRegistry->clear();
|
||||
|
||||
}
|
||||
|
||||
void ServerContextImpl::printInfo()
|
||||
{
|
||||
printInfo(cout);
|
||||
}
|
||||
|
||||
void ServerContextImpl::printInfo(ostream& str)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
str << "VERSION : " << getVersion().getVersionString() << endl \
|
||||
<< "PROVIDER_NAMES : " << _channelProviderNames << endl \
|
||||
<< "BEACON_ADDR_LIST : " << _beaconAddressList << endl \
|
||||
<< "AUTO_BEACON_ADDR_LIST : " << _autoBeaconAddressList << endl \
|
||||
<< "BEACON_PERIOD : " << _beaconPeriod << endl \
|
||||
<< "BROADCAST_PORT : " << _broadcastPort << endl \
|
||||
<< "SERVER_PORT : " << _serverPort << endl \
|
||||
<< "RCV_BUFFER_SIZE : " << _receiveBufferSize << endl \
|
||||
<< "IGNORE_ADDR_LIST: " << _ignoreAddressList << endl \
|
||||
<< "STATE : " << ServerContextImpl::StateNames[_state] << endl;
|
||||
}
|
||||
|
||||
void ServerContextImpl::dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
void ServerContextImpl::setBeaconServerStatusProvider(BeaconServerStatusProvider::shared_pointer const & beaconServerStatusProvider)
|
||||
{
|
||||
_beaconServerStatusProvider = beaconServerStatusProvider;
|
||||
}
|
||||
|
||||
bool ServerContextImpl::isInitialized()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
return _state == INITIALIZED || _state == RUNNING || _state == SHUTDOWN;
|
||||
}
|
||||
|
||||
bool ServerContextImpl::isDestroyed()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
return _state == DESTROYED;
|
||||
}
|
||||
|
||||
std::string ServerContextImpl::getBeaconAddressList()
|
||||
{
|
||||
return _beaconAddressList;
|
||||
}
|
||||
|
||||
bool ServerContextImpl::isAutoBeaconAddressList()
|
||||
{
|
||||
return _autoBeaconAddressList;
|
||||
}
|
||||
|
||||
float ServerContextImpl::getBeaconPeriod()
|
||||
{
|
||||
return _beaconPeriod;
|
||||
}
|
||||
|
||||
int32 ServerContextImpl::getReceiveBufferSize()
|
||||
{
|
||||
return _receiveBufferSize;
|
||||
}
|
||||
|
||||
int32 ServerContextImpl::getServerPort()
|
||||
{
|
||||
return _serverPort;
|
||||
}
|
||||
|
||||
void ServerContextImpl::setServerPort(int32 port)
|
||||
{
|
||||
_serverPort = port;
|
||||
}
|
||||
|
||||
int32 ServerContextImpl::getBroadcastPort()
|
||||
{
|
||||
return _broadcastPort;
|
||||
}
|
||||
|
||||
std::string ServerContextImpl::getIgnoreAddressList()
|
||||
{
|
||||
return _ignoreAddressList;
|
||||
}
|
||||
|
||||
BeaconServerStatusProvider::shared_pointer ServerContextImpl::getBeaconServerStatusProvider()
|
||||
{
|
||||
return _beaconServerStatusProvider;
|
||||
}
|
||||
|
||||
osiSockAddr* ServerContextImpl::getServerInetAddress()
|
||||
{
|
||||
if(_acceptor != NULL)
|
||||
{
|
||||
return const_cast<osiSockAddr*>(_acceptor->getBindAddress());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BlockingUDPTransport::shared_pointer ServerContextImpl::getBroadcastTransport()
|
||||
{
|
||||
return _broadcastTransport;
|
||||
}
|
||||
|
||||
ChannelAccess::shared_pointer ServerContextImpl::getChannelAccess()
|
||||
{
|
||||
return _channelAccess;
|
||||
}
|
||||
|
||||
std::string ServerContextImpl::getChannelProviderName()
|
||||
{
|
||||
return _channelProviderNames;
|
||||
}
|
||||
|
||||
// NOTE: not synced
|
||||
void ServerContextImpl::setChannelProviderName(std::string channelProviderName)
|
||||
{
|
||||
if (_state != NOT_INITIALIZED)
|
||||
throw std::logic_error("must be called before initialize");
|
||||
_channelProviderNames = channelProviderName;
|
||||
}
|
||||
|
||||
std::vector<ChannelProvider::shared_pointer> ServerContextImpl::getChannelProviders()
|
||||
{
|
||||
return _channelProviders;
|
||||
}
|
||||
|
||||
Timer::shared_pointer ServerContextImpl::getTimer()
|
||||
{
|
||||
return _timer;
|
||||
}
|
||||
|
||||
TransportRegistry::shared_pointer ServerContextImpl::getTransportRegistry()
|
||||
{
|
||||
return _transportRegistry;
|
||||
}
|
||||
|
||||
Channel::shared_pointer ServerContextImpl::getChannel(pvAccessID /*id*/)
|
||||
{
|
||||
// not used
|
||||
return Channel::shared_pointer();
|
||||
}
|
||||
|
||||
Transport::shared_pointer ServerContextImpl::getSearchTransport()
|
||||
{
|
||||
// not used
|
||||
return Transport::shared_pointer();
|
||||
}
|
||||
|
||||
void ServerContextImpl::newServerDetected()
|
||||
{
|
||||
// not used
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct ThreadRunnerParam {
|
||||
ServerContextImpl::shared_pointer ctx;
|
||||
int timeToRun;
|
||||
};
|
||||
|
||||
static void threadRunner(void* usr)
|
||||
{
|
||||
ThreadRunnerParam* pusr = static_cast<ThreadRunnerParam*>(usr);
|
||||
ThreadRunnerParam param = *pusr;
|
||||
delete pusr;
|
||||
|
||||
param.ctx->run(param.timeToRun);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ServerContext::shared_pointer startPVAServer(String const & providerNames, int timeToRun, bool runInSeparateThread, bool printInfo)
|
||||
{
|
||||
ServerContextImpl::shared_pointer ctx = ServerContextImpl::create();
|
||||
|
||||
// do not override configuration
|
||||
if (!ctx->isChannelProviderNamePreconfigured())
|
||||
ctx->setChannelProviderName(providerNames);
|
||||
|
||||
ChannelAccess::shared_pointer channelAccess = getChannelAccess();
|
||||
ctx->initialize(channelAccess);
|
||||
|
||||
if (printInfo)
|
||||
ctx->printInfo();
|
||||
|
||||
|
||||
if (runInSeparateThread)
|
||||
{
|
||||
// delete left to the thread
|
||||
auto_ptr<ThreadRunnerParam> param(new ThreadRunnerParam());
|
||||
param->ctx = ctx;
|
||||
param->timeToRun = timeToRun;
|
||||
|
||||
// TODO can this fail?
|
||||
epicsThreadCreate("startPVAServer",
|
||||
epicsThreadPriorityMedium,
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
threadRunner, param.get());
|
||||
|
||||
param.release();
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->run(timeToRun);
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
427
src/server/serverContext.h
Normal file
427
src/server/serverContext.h
Normal file
@@ -0,0 +1,427 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SERVERCONTEXT_H_
|
||||
#define SERVERCONTEXT_H_
|
||||
|
||||
#include <pv/remote.h>
|
||||
#include <pv/beaconServerStatusProvider.h>
|
||||
#include <pv/pvaConstants.h>
|
||||
#include <pv/pvaVersion.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/blockingUDP.h>
|
||||
#include <pv/blockingTCP.h>
|
||||
#include <pv/beaconEmitter.h>
|
||||
#include <pv/logger.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
/**
|
||||
* The class representing a PVA Server context.
|
||||
*/
|
||||
class epicsShareClass ServerContext
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<ServerContext> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const ServerContext> const_shared_pointer;
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~ServerContext() {};
|
||||
/**
|
||||
* Get context implementation version.
|
||||
* @return version of the context implementation.
|
||||
*/
|
||||
virtual const Version& getVersion() = 0;
|
||||
|
||||
/**
|
||||
* Set <code>ChannelAccess</code> implementation and initialize server.
|
||||
* @param channelAccess implementation of channel access to be served.
|
||||
*/
|
||||
virtual void initialize(ChannelAccess::shared_pointer const & channelAccess) = 0;
|
||||
|
||||
/**
|
||||
* Run server (process events).
|
||||
* @param seconds time in seconds the server will process events (method will block), if <code>0</code>
|
||||
* the method would block until <code>destroy()</code> is called.
|
||||
* @throws BaseException if server is already destroyed.
|
||||
*/
|
||||
virtual void run(epics::pvData::int32 seconds) = 0;
|
||||
|
||||
/**
|
||||
* Shutdown (stop executing run() method) of this context.
|
||||
* After shutdown Context cannot be rerun again, destroy() has to be called to clear all used resources.
|
||||
* @throws BaseException if the context has been destroyed.
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* Clear all resources attached to this context.
|
||||
* @throws BaseException if the context has been destroyed.
|
||||
*/
|
||||
virtual void destroy() = 0;
|
||||
|
||||
/**
|
||||
* Prints detailed information about the context to the standard output stream.
|
||||
*/
|
||||
virtual void printInfo() = 0;
|
||||
|
||||
/**
|
||||
* Prints detailed information about the context to the specified output stream.
|
||||
* @param str stream to which to print the info
|
||||
*/
|
||||
virtual void printInfo(std::ostream& str) = 0;
|
||||
|
||||
/**
|
||||
* Dispose (destroy) server context.
|
||||
* This calls <code>destroy()</code> and silently handles all exceptions.
|
||||
*/
|
||||
virtual void dispose() = 0;
|
||||
|
||||
// ************************************************************************** //
|
||||
// **************************** [ Plugins ] ********************************* //
|
||||
// ************************************************************************** //
|
||||
|
||||
/**
|
||||
* Set beacon server status provider.
|
||||
* @param beaconServerStatusProvider <code>BeaconServerStatusProvider</code> implementation to set.
|
||||
*/
|
||||
virtual void setBeaconServerStatusProvider(BeaconServerStatusProvider::shared_pointer const & beaconServerStatusProvider) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class epicsShareClass ServerContextImpl :
|
||||
public ServerContext,
|
||||
public Context,
|
||||
public ResponseHandlerFactory,
|
||||
public std::tr1::enable_shared_from_this<ServerContextImpl>
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<ServerContextImpl> shared_pointer;
|
||||
typedef std::tr1::shared_ptr<const ServerContextImpl> const_shared_pointer;
|
||||
protected:
|
||||
ServerContextImpl();
|
||||
public:
|
||||
static shared_pointer create();
|
||||
|
||||
virtual ~ServerContextImpl();
|
||||
|
||||
//**************** derived from ServerContext ****************//
|
||||
const Version& getVersion();
|
||||
void initialize(ChannelAccess::shared_pointer const & channelAccess);
|
||||
void run(epics::pvData::int32 seconds);
|
||||
void shutdown();
|
||||
void destroy();
|
||||
void printInfo();
|
||||
void printInfo(std::ostream& str);
|
||||
void dispose();
|
||||
void setBeaconServerStatusProvider(BeaconServerStatusProvider::shared_pointer const & beaconServerStatusProvider);
|
||||
//**************** derived from Context ****************//
|
||||
epics::pvData::Timer::shared_pointer getTimer();
|
||||
Channel::shared_pointer getChannel(pvAccessID id);
|
||||
Transport::shared_pointer getSearchTransport();
|
||||
Configuration::shared_pointer getConfiguration();
|
||||
TransportRegistry::shared_pointer getTransportRegistry();
|
||||
|
||||
std::auto_ptr<ResponseHandler> createResponseHandler();
|
||||
virtual void newServerDetected();
|
||||
|
||||
/**
|
||||
* Version.
|
||||
*/
|
||||
static const Version VERSION;
|
||||
|
||||
|
||||
/**
|
||||
* Server state enum.
|
||||
*/
|
||||
enum State {
|
||||
/**
|
||||
* State value of non-initialized context.
|
||||
*/
|
||||
NOT_INITIALIZED,
|
||||
|
||||
/**
|
||||
* State value of initialized context.
|
||||
*/
|
||||
INITIALIZED,
|
||||
|
||||
/**
|
||||
* State value of running context.
|
||||
*/
|
||||
RUNNING,
|
||||
|
||||
/**
|
||||
* State value of shutdown (once running) context.
|
||||
*/
|
||||
SHUTDOWN,
|
||||
|
||||
/**
|
||||
* State value of destroyed context.
|
||||
*/
|
||||
DESTROYED
|
||||
};
|
||||
/**
|
||||
* Names of the enum <code>State</code>
|
||||
*/
|
||||
static const char* StateNames[];
|
||||
|
||||
/**
|
||||
* Get initialization status.
|
||||
* @return initialization status.
|
||||
*/
|
||||
bool isInitialized();
|
||||
|
||||
/**
|
||||
* Get destruction status.
|
||||
* @return destruction status.
|
||||
*/
|
||||
bool isDestroyed();
|
||||
|
||||
/**
|
||||
* Get beacon address list.
|
||||
* @return beacon address list.
|
||||
*/
|
||||
std::string getBeaconAddressList();
|
||||
|
||||
/**
|
||||
* Get beacon address list auto flag.
|
||||
* @return beacon address list auto flag.
|
||||
*/
|
||||
bool isAutoBeaconAddressList();
|
||||
|
||||
/**
|
||||
* Get beacon period (in seconds).
|
||||
* @return beacon period (in seconds).
|
||||
*/
|
||||
float getBeaconPeriod();
|
||||
|
||||
/**
|
||||
* Get receiver buffer (payload) size.
|
||||
* @return max payload size.
|
||||
*/
|
||||
epics::pvData::int32 getReceiveBufferSize();
|
||||
|
||||
/**
|
||||
* Get server port.
|
||||
* @return server port.
|
||||
*/
|
||||
epics::pvData::int32 getServerPort();
|
||||
|
||||
/**
|
||||
* Set server port number.
|
||||
* @param port new server port number.
|
||||
*/
|
||||
void setServerPort(epics::pvData::int32 port);
|
||||
|
||||
/**
|
||||
* Get broadcast port.
|
||||
* @return broadcast port.
|
||||
*/
|
||||
epics::pvData::int32 getBroadcastPort();
|
||||
|
||||
/**
|
||||
* Get ignore search address list.
|
||||
* @return ignore search address list.
|
||||
*/
|
||||
std::string getIgnoreAddressList();
|
||||
|
||||
/**
|
||||
* Get registered beacon server status provider.
|
||||
* @return registered beacon server status provider.
|
||||
*/
|
||||
BeaconServerStatusProvider::shared_pointer getBeaconServerStatusProvider();
|
||||
|
||||
/**
|
||||
* Get server newtwork (IP) address.
|
||||
* @return server network (IP) address, <code>NULL</code> if not bounded.
|
||||
*/
|
||||
osiSockAddr* getServerInetAddress();
|
||||
|
||||
/**
|
||||
* Broadcast transport.
|
||||
* @return broadcast transport.
|
||||
*/
|
||||
BlockingUDPTransport::shared_pointer getBroadcastTransport();
|
||||
|
||||
/**
|
||||
* Get channel access implementation.
|
||||
* @return channel access implementation.
|
||||
*/
|
||||
ChannelAccess::shared_pointer getChannelAccess();
|
||||
|
||||
/**
|
||||
* Get channel provider name.
|
||||
* @return channel provider name.
|
||||
*/
|
||||
std::string getChannelProviderName();
|
||||
|
||||
/**
|
||||
* Set channel provider name.
|
||||
* This method can only be called before initialize.
|
||||
*/
|
||||
void setChannelProviderName(std::string providerName);
|
||||
|
||||
/**
|
||||
* Get channel providers.
|
||||
* @return channel providers.
|
||||
*/
|
||||
std::vector<ChannelProvider::shared_pointer> getChannelProviders();
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if channel provider name is provided by configuration (e.g. system env. var.).
|
||||
* @return <code>true</code> if channel provider name is provided by configuration (e.g. system env. var.)
|
||||
*/
|
||||
bool isChannelProviderNamePreconfigured();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Initialization status.
|
||||
*/
|
||||
State _state;
|
||||
|
||||
/**
|
||||
* A space-separated list of broadcast address which to send beacons.
|
||||
* Each address must be of the form: ip.number:port or host.name:port
|
||||
*/
|
||||
std::string _beaconAddressList;
|
||||
|
||||
/**
|
||||
* A space-separated list of address from which to ignore name resolution requests.
|
||||
* Each address must be of the form: ip.number:port or host.name:port
|
||||
*/
|
||||
std::string _ignoreAddressList;
|
||||
|
||||
/**
|
||||
* Define whether or not the network interfaces should be discovered at runtime.
|
||||
*/
|
||||
bool _autoBeaconAddressList;
|
||||
|
||||
/**
|
||||
* Period in second between two beacon signals.
|
||||
*/
|
||||
float _beaconPeriod;
|
||||
|
||||
/**
|
||||
* Broadcast port number to listen to.
|
||||
*/
|
||||
epics::pvData::int32 _broadcastPort;
|
||||
|
||||
/**
|
||||
* Port number for the server to listen to.
|
||||
*/
|
||||
epics::pvData::int32 _serverPort;
|
||||
|
||||
/**
|
||||
* Length in bytes of the maximum buffer (payload) size that may pass through PVA.
|
||||
*/
|
||||
epics::pvData::int32 _receiveBufferSize;
|
||||
|
||||
/**
|
||||
* Timer.
|
||||
*/
|
||||
epics::pvData::Timer::shared_pointer _timer;
|
||||
|
||||
/**
|
||||
* Broadcast transport needed for channel searches.
|
||||
*/
|
||||
BlockingUDPTransport::shared_pointer _broadcastTransport;
|
||||
|
||||
/**
|
||||
* Beacon emitter.
|
||||
*/
|
||||
BeaconEmitter::shared_pointer _beaconEmitter;
|
||||
|
||||
/**
|
||||
* PVAS acceptor (accepts PVA virtual circuit).
|
||||
*/
|
||||
BlockingTCPAcceptor::shared_pointer _acceptor;
|
||||
|
||||
/**
|
||||
* PVA transport (virtual circuit) registry.
|
||||
* This registry contains all active transports - connections to PVA servers.
|
||||
*/
|
||||
TransportRegistry::shared_pointer _transportRegistry;
|
||||
|
||||
/**
|
||||
* Channel access.
|
||||
*/
|
||||
ChannelAccess::shared_pointer _channelAccess;
|
||||
|
||||
/**
|
||||
* Channel provider name.
|
||||
*/
|
||||
std::string _channelProviderNames;
|
||||
|
||||
/**
|
||||
* Channel provider.
|
||||
*/
|
||||
std::vector<ChannelProvider::shared_pointer> _channelProviders;
|
||||
|
||||
/**
|
||||
* Run mutex.
|
||||
*/
|
||||
epics::pvData::Mutex _mutex;
|
||||
|
||||
/**
|
||||
* Run event.
|
||||
*/
|
||||
epics::pvData::Event _runEvent;
|
||||
|
||||
/**
|
||||
* Beacon server status provider interface (optional).
|
||||
*/
|
||||
BeaconServerStatusProvider::shared_pointer _beaconServerStatusProvider;
|
||||
|
||||
/**
|
||||
* Initialize logger.
|
||||
*/
|
||||
void initializeLogger();
|
||||
|
||||
/**
|
||||
* Load configuration.
|
||||
*/
|
||||
void loadConfiguration();
|
||||
|
||||
/**
|
||||
* Internal initialization.
|
||||
*/
|
||||
void internalInitialize();
|
||||
|
||||
/**
|
||||
* Initialize broadcast DP transport (broadcast socket and repeater connection).
|
||||
*/
|
||||
void initializeBroadcastTransport();
|
||||
|
||||
/**
|
||||
* Internal destroy.
|
||||
*/
|
||||
void internalDestroy();
|
||||
|
||||
/**
|
||||
* Destroy all transports.
|
||||
*/
|
||||
void destroyAllTransports();
|
||||
|
||||
Configuration::shared_pointer configuration;
|
||||
};
|
||||
|
||||
epicsShareExtern ServerContext::shared_pointer startPVAServer(
|
||||
epics::pvData::String const & providerNames = PVACCESS_ALL_PROVIDERS,
|
||||
int timeToRun = 0,
|
||||
bool runInSeparateThread = false,
|
||||
bool printInfo = false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* SERVERCONTEXT_H_ */
|
||||
19
src/utils/Makefile
Normal file
19
src/utils/Makefile
Normal file
@@ -0,0 +1,19 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/utils
|
||||
|
||||
INC += hexDump.h
|
||||
INC += inetAddressUtil.h
|
||||
INC += logger.h
|
||||
INC += introspectionRegistry.h
|
||||
INC += namedLockPattern.h
|
||||
INC += referenceCountingLock.h
|
||||
INC += configuration.h
|
||||
INC += likely.h
|
||||
|
||||
LIBSRCS += hexDump.cpp
|
||||
LIBSRCS += inetAddressUtil.cpp
|
||||
LIBSRCS += logger.cpp
|
||||
LIBSRCS += introspectionRegistry.cpp
|
||||
LIBSRCS += configuration.cpp
|
||||
LIBSRCS += referenceCountingLock.cpp
|
||||
397
src/utils/configuration.cpp
Normal file
397
src/utils/configuration.cpp
Normal file
@@ -0,0 +1,397 @@
|
||||
/**
|
||||
* 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 <algorithm>
|
||||
|
||||
#include <pv/epicsException.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/configuration.h>
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ < 3
|
||||
#define OLDGCC
|
||||
#define NO_STREAM_EXCEPTIONS
|
||||
#endif
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
|
||||
Properties::Properties()
|
||||
{
|
||||
_fileName = "";
|
||||
_infile.reset(new ifstream());
|
||||
_infile->exceptions (ifstream::failbit | ifstream::badbit );
|
||||
_outfile.reset(new ofstream());
|
||||
_outfile->exceptions (ofstream::failbit | ofstream::badbit );
|
||||
}
|
||||
|
||||
Properties::Properties(const string &fileName)
|
||||
{
|
||||
_fileName = fileName;
|
||||
_infile.reset(new ifstream());
|
||||
_infile->exceptions (ifstream::failbit | ifstream::badbit );
|
||||
_outfile.reset(new ofstream());
|
||||
_outfile->exceptions (ofstream::failbit | ofstream::badbit );
|
||||
}
|
||||
|
||||
Properties::~Properties()
|
||||
{
|
||||
}
|
||||
|
||||
void Properties::setProperty(const string &key, const string &value)
|
||||
{
|
||||
string oldValue;
|
||||
std::map<std::string,std::string>::iterator propertiesIterator = _properties.find(key);
|
||||
|
||||
if(propertiesIterator != _properties.end()) //found in map
|
||||
{
|
||||
_properties.erase(propertiesIterator);
|
||||
}
|
||||
_properties[key] = value;
|
||||
}
|
||||
|
||||
string Properties::getProperty(const string &key)
|
||||
{
|
||||
std::map<std::string,std::string>::iterator propertiesIterator = _properties.find(key);
|
||||
if(propertiesIterator != _properties.end()) //found in map
|
||||
{
|
||||
return string(propertiesIterator->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
string errMsg = "Property not found in the map: " + key;
|
||||
THROW_BASE_EXCEPTION(errMsg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
string Properties::getProperty(const string &key, const string &defaultValue)
|
||||
{
|
||||
std::map<std::string,std::string>::iterator propertiesIterator = _properties.find(key);
|
||||
if(propertiesIterator != _properties.end()) //found in map
|
||||
{
|
||||
return string(propertiesIterator->second);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
bool Properties::hasProperty(const string &key)
|
||||
{
|
||||
return (_properties.find(key) != _properties.end());
|
||||
}
|
||||
|
||||
void Properties::load()
|
||||
{
|
||||
_properties.clear();
|
||||
|
||||
#ifdef NO_STREAM_EXCEPTIONS
|
||||
_infile->open(_fileName.c_str(),ifstream::in);
|
||||
if (_infile->fail())
|
||||
#else
|
||||
try
|
||||
{
|
||||
_infile->open(_fileName.c_str(),ifstream::in);
|
||||
}
|
||||
catch (ifstream::failure& e)
|
||||
#endif
|
||||
{
|
||||
string errMsg = "Error opening file: " + string(_fileName.c_str());
|
||||
THROW_BASE_EXCEPTION(errMsg.c_str());
|
||||
}
|
||||
|
||||
string line;
|
||||
string property;
|
||||
string key;
|
||||
#ifndef NO_STREAM_EXCEPTIONS
|
||||
try
|
||||
{
|
||||
#endif
|
||||
while(!_infile->eof())
|
||||
{
|
||||
line.erase();
|
||||
std::getline(*_infile,line);
|
||||
|
||||
#ifdef NO_STREAM_EXCEPTIONS
|
||||
if (_infile->fail())
|
||||
{
|
||||
_infile->close();
|
||||
if(_infile->eof())
|
||||
{
|
||||
return; //end of file
|
||||
}
|
||||
string errMsg = "Error reading file: " + _fileName;
|
||||
THROW_BASE_EXCEPTION(errMsg.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
//remove trailing spaces
|
||||
truncate(line);
|
||||
|
||||
//empty line
|
||||
if(line.length() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// comment
|
||||
if(line[0] == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//line is in format: propertyName=propertyValue
|
||||
size_t pos = line.find_first_of('=',0);
|
||||
if(pos == string::npos) //bad value (= not found)
|
||||
{
|
||||
string errMsg = "Bad property line found: " + line;
|
||||
THROW_BASE_EXCEPTION(errMsg.c_str());
|
||||
}
|
||||
|
||||
key = line.substr(0,pos);
|
||||
truncate(key);
|
||||
property = line.substr(pos + 1,line.length());
|
||||
truncate(property);
|
||||
_properties[key] = property;
|
||||
}
|
||||
#ifndef NO_STREAM_EXCEPTIONS
|
||||
}
|
||||
catch (ifstream::failure& e)
|
||||
{
|
||||
_infile->close();
|
||||
if(_infile->eof())
|
||||
{
|
||||
return; //end of file
|
||||
}
|
||||
string errMsg = "Error reading file: " + _fileName;
|
||||
THROW_BASE_EXCEPTION(errMsg.c_str());
|
||||
}
|
||||
#endif
|
||||
_infile->close();
|
||||
}
|
||||
|
||||
void Properties::load(const string &fileName)
|
||||
{
|
||||
_fileName = fileName;
|
||||
load();
|
||||
}
|
||||
|
||||
void Properties::store()
|
||||
{
|
||||
#ifdef NO_STREAM_EXCEPTIONS
|
||||
_outfile->open(_fileName.c_str(),ifstream::trunc);
|
||||
if (_outfile->fail())
|
||||
#else
|
||||
try
|
||||
{
|
||||
_outfile->open(_fileName.c_str(),ifstream::trunc);
|
||||
}
|
||||
catch (ofstream::failure& e)
|
||||
#endif
|
||||
{
|
||||
string errMsg = "Error opening file: " + string(_fileName.c_str());
|
||||
THROW_BASE_EXCEPTION(errMsg.c_str());
|
||||
}
|
||||
|
||||
|
||||
for (std::map<std::string,std::string>::iterator propertiesIterator = _properties.begin();
|
||||
propertiesIterator != _properties.end();
|
||||
propertiesIterator++ )
|
||||
{
|
||||
#ifndef NO_STREAM_EXCEPTIONS
|
||||
try
|
||||
{
|
||||
#endif
|
||||
string line = string(propertiesIterator->first) + string("=") + string(propertiesIterator->second) + string("\n");
|
||||
_outfile->write(line.c_str(),line.length());
|
||||
#ifdef NO_STREAM_EXCEPTIONS
|
||||
if(_outfile->fail())
|
||||
#else
|
||||
}
|
||||
catch (ofstream::failure& e)
|
||||
#endif
|
||||
{
|
||||
_outfile->close();
|
||||
string errMsg = "Error writing to file: " + string(_fileName.c_str());
|
||||
THROW_BASE_EXCEPTION(errMsg.c_str());
|
||||
}
|
||||
}
|
||||
_outfile->close();
|
||||
}
|
||||
|
||||
void Properties::store(const string &fileName)
|
||||
{
|
||||
_fileName = fileName;
|
||||
store();
|
||||
}
|
||||
|
||||
void Properties::list()
|
||||
{
|
||||
for (std::map<std::string,std::string>::iterator propertiesIterator = _properties.begin() ;
|
||||
propertiesIterator != _properties.end();
|
||||
propertiesIterator++ )
|
||||
{
|
||||
cout << "Key:" << propertiesIterator->first << ",Value: " << propertiesIterator->second << endl;
|
||||
}
|
||||
}
|
||||
|
||||
SystemConfigurationImpl::SystemConfigurationImpl() :
|
||||
_properties(new Properties())
|
||||
{
|
||||
_envParam.name = new char[256];
|
||||
_envParam.pdflt = NULL;
|
||||
// no exception, default value is taken
|
||||
//_ibuffer.exceptions ( ifstream::failbit | ifstream::badbit );
|
||||
//_obuffer.exceptions ( ifstream::failbit | ifstream::badbit );
|
||||
}
|
||||
|
||||
SystemConfigurationImpl::~SystemConfigurationImpl()
|
||||
{
|
||||
if(_envParam.name) delete[] _envParam.name;
|
||||
}
|
||||
|
||||
bool SystemConfigurationImpl::getPropertyAsBoolean(const string &name, const bool defaultValue)
|
||||
{
|
||||
/*
|
||||
bool retval;
|
||||
_ibuffer.clear();
|
||||
_obuffer.clear();
|
||||
_obuffer.str("");
|
||||
_obuffer << defaultValue;
|
||||
_ibuffer.str(getPropertyAsString(name,_obuffer.str()));
|
||||
_ibuffer >> retval;
|
||||
if (_ibuffer.fail() || _ibuffer.bad())
|
||||
return defaultValue;
|
||||
else
|
||||
return retval;
|
||||
*/
|
||||
|
||||
string value = getPropertyAsString(name, defaultValue ? "1" : "0");
|
||||
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
|
||||
|
||||
bool isTrue = (value == "1") || (value == "true") || (value == "yes");
|
||||
bool isFalse = (value == "0") || (value == "false") || (value == "no");
|
||||
|
||||
// invalid value
|
||||
if (!(isTrue || isFalse))
|
||||
return defaultValue;
|
||||
else
|
||||
return isTrue == true;
|
||||
}
|
||||
|
||||
int32 SystemConfigurationImpl::getPropertyAsInteger(const string &name, const int32 defaultValue)
|
||||
{
|
||||
int32 retval;
|
||||
_ibuffer.clear();
|
||||
_obuffer.clear();
|
||||
_obuffer.str("");
|
||||
_obuffer << defaultValue;
|
||||
_ibuffer.str(getPropertyAsString(name, _obuffer.str()));
|
||||
_ibuffer >> retval;
|
||||
if (_ibuffer.fail() || _ibuffer.bad())
|
||||
return defaultValue;
|
||||
else
|
||||
return retval;
|
||||
}
|
||||
|
||||
float SystemConfigurationImpl::getPropertyAsFloat(const string &name, const float defaultValue)
|
||||
{
|
||||
float retval;
|
||||
_ibuffer.clear();
|
||||
_obuffer.clear();
|
||||
_obuffer.str("");
|
||||
_obuffer << defaultValue;
|
||||
_ibuffer.str(getPropertyAsString(name, _obuffer.str()));
|
||||
_ibuffer >> retval;
|
||||
if (_ibuffer.fail() || _ibuffer.bad())
|
||||
return defaultValue;
|
||||
else
|
||||
return retval;
|
||||
}
|
||||
|
||||
float SystemConfigurationImpl::getPropertyAsDouble(const string &name, const double defaultValue)
|
||||
{
|
||||
float retval;
|
||||
_ibuffer.clear();
|
||||
_obuffer.clear();
|
||||
_obuffer.str("");
|
||||
_obuffer << defaultValue;
|
||||
_ibuffer.str(getPropertyAsString(name, _obuffer.str()));
|
||||
_ibuffer >> retval;
|
||||
if (_ibuffer.fail() || _ibuffer.bad())
|
||||
return defaultValue;
|
||||
else
|
||||
return retval;
|
||||
}
|
||||
|
||||
string SystemConfigurationImpl::getPropertyAsString(const string &name, const string &defaultValue)
|
||||
{
|
||||
strncpy(_envParam.name,name.c_str(),name.length() + 1);
|
||||
const char* val = envGetConfigParamPtr(&_envParam);
|
||||
if(val != NULL)
|
||||
{
|
||||
return _properties->getProperty(name, string(val));
|
||||
}
|
||||
return _properties->getProperty(name, defaultValue);
|
||||
}
|
||||
|
||||
bool SystemConfigurationImpl::hasProperty(const string &key)
|
||||
{
|
||||
strncpy(_envParam.name,key.c_str(),key.length() + 1);
|
||||
const char* val = envGetConfigParamPtr(&_envParam);
|
||||
return (val != NULL) || _properties->hasProperty(key);
|
||||
}
|
||||
|
||||
ConfigurationProviderImpl::ConfigurationProviderImpl()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ConfigurationProviderImpl::~ConfigurationProviderImpl()
|
||||
{
|
||||
}
|
||||
|
||||
void ConfigurationProviderImpl::registerConfiguration(const string &name, Configuration::shared_pointer const & configuration)
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
std::map<std::string,Configuration::shared_pointer>::iterator configsIter = _configs.find(name);
|
||||
if(configsIter != _configs.end())
|
||||
{
|
||||
string msg = "configuration with name " + name + " already registered";
|
||||
THROW_BASE_EXCEPTION(msg.c_str());
|
||||
}
|
||||
_configs[name] = configuration;
|
||||
}
|
||||
|
||||
Configuration::shared_pointer ConfigurationProviderImpl::getConfiguration(const string &name)
|
||||
{
|
||||
std::map<std::string,Configuration::shared_pointer>::iterator configsIter = _configs.find(name);
|
||||
if(configsIter != _configs.end())
|
||||
{
|
||||
return configsIter->second;
|
||||
}
|
||||
return Configuration::shared_pointer();
|
||||
}
|
||||
|
||||
ConfigurationProvider::shared_pointer configurationProvider;
|
||||
Mutex conf_factory_mutex;
|
||||
|
||||
ConfigurationProvider::shared_pointer ConfigurationFactory::getProvider()
|
||||
{
|
||||
Lock guard(conf_factory_mutex);
|
||||
if(configurationProvider.get() == NULL)
|
||||
{
|
||||
configurationProvider.reset(new ConfigurationProviderImpl());
|
||||
// default
|
||||
Configuration::shared_pointer systemConfig(new SystemConfigurationImpl());
|
||||
configurationProvider->registerConfiguration("system", systemConfig);
|
||||
}
|
||||
return configurationProvider;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
228
src/utils/configuration.h
Normal file
228
src/utils/configuration.h
Normal file
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CONFIGURATION_H
|
||||
#define CONFIGURATION_H
|
||||
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define configurationEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/noDefaultMethods.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <envDefs.h>
|
||||
#ifdef configurationEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef configurationEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class epicsShareClass Properties
|
||||
{
|
||||
public:
|
||||
Properties();
|
||||
Properties(const std::string &fileName);
|
||||
virtual ~Properties();
|
||||
|
||||
void setProperty(const std::string &key,const std::string &value);
|
||||
std::string getProperty(const std::string &key);
|
||||
std::string getProperty(const std::string &key, const std::string &defaultValue);
|
||||
bool hasProperty(const std::string &key);
|
||||
|
||||
void store();
|
||||
void store(const std::string &fileName);
|
||||
void load();
|
||||
void load(const std::string &fileName);
|
||||
void list();
|
||||
|
||||
private:
|
||||
std::map<std::string,std::string> _properties;
|
||||
std::auto_ptr<std::ifstream> _infile;
|
||||
std::auto_ptr<std::ofstream> _outfile;
|
||||
std::string _fileName;
|
||||
|
||||
inline void truncate(std::string& str)
|
||||
{
|
||||
while(str.length() != 0 && (str.at(0) == ' ' || str.at(0) == '\t'))
|
||||
{
|
||||
str.erase(0,1);
|
||||
}
|
||||
while(str.length() != 0 && (str.at(str.length()-1) == ' ' || str.at(str.length()-1) == '\t'))
|
||||
{
|
||||
str.erase(str.length()-1,1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
*/
|
||||
class epicsShareClass Configuration : private epics::pvData::NoDefaultMethods
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(Configuration);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Configuration() {};
|
||||
/**
|
||||
* Get the environment variable specified by name or return default value
|
||||
* if it does not exist.
|
||||
*
|
||||
* @param name name of the environment variable to return.
|
||||
* @param defualtValue default value to return if environment variable does not exists.
|
||||
*
|
||||
* @return environment variable value as bool or default value if it does not exist.
|
||||
*/
|
||||
virtual bool getPropertyAsBoolean(const std::string &name, const bool defaultValue) = 0;
|
||||
/**
|
||||
* Get the environment variable specified by name or return default value
|
||||
* if it does not exist.
|
||||
*
|
||||
* @param name name of the environment variable to return.
|
||||
* @param defualtValue default value to return if environment variable does not exists.
|
||||
*
|
||||
* @return environment variable value as int32 or default value if it does not exist.
|
||||
*/
|
||||
virtual epics::pvData::int32 getPropertyAsInteger(const std::string &name, const epics::pvData::int32 defaultValue) = 0;
|
||||
/**
|
||||
* Get the environment variable specified by name or return default value
|
||||
* if it does not exist.
|
||||
*
|
||||
* @param name name of the environment variable to return.
|
||||
* @param defualtValue default value to return if environment variable does not exists.
|
||||
*
|
||||
* @return environment variable value as float or default value if it does not exist.
|
||||
*/
|
||||
virtual float getPropertyAsFloat(const std::string &name, const float defaultValue) = 0;
|
||||
/**
|
||||
* Get the environment variable specified by name or return default value
|
||||
* if it does not exist.
|
||||
*
|
||||
* @param name name of the environment variable to return.
|
||||
* @param defualtValue default value to return if environment variable does not exists.
|
||||
*
|
||||
* @return environment variable value as double or default value if it does not exist.
|
||||
*/
|
||||
virtual float getPropertyAsDouble(const std::string &name, const double defaultValue) = 0;
|
||||
/**
|
||||
* Get the environment variable specified by name or return default value
|
||||
* if it does not exist.
|
||||
*
|
||||
* @param name name of the environment variable to return.
|
||||
* @param defualtValue default value to return if environment variable does not exists.
|
||||
*
|
||||
* @return environment variable value as std::string or default value if it does not exist.
|
||||
*/
|
||||
virtual std::string getPropertyAsString(const std::string &name, const std::string &defaultValue) = 0;
|
||||
|
||||
virtual bool hasProperty(const std::string &name) = 0;
|
||||
};
|
||||
|
||||
class epicsShareClass SystemConfigurationImpl: public Configuration
|
||||
{
|
||||
public:
|
||||
SystemConfigurationImpl();
|
||||
~SystemConfigurationImpl();
|
||||
bool getPropertyAsBoolean(const std::string &name, const bool defaultValue);
|
||||
epics::pvData::int32 getPropertyAsInteger(const std::string &name, const epics::pvData::int32 defaultValue);
|
||||
float getPropertyAsFloat(const std::string &name, const float defaultValue);
|
||||
float getPropertyAsDouble(const std::string &name, const double defaultValue);
|
||||
std::string getPropertyAsString(const std::string &name, const std::string &defaultValue);
|
||||
bool hasProperty(const std::string &name);
|
||||
std::auto_ptr<Properties> _properties;
|
||||
private:
|
||||
ENV_PARAM _envParam;
|
||||
std::istringstream _ibuffer;
|
||||
std::ostringstream _obuffer;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Configuration provider.
|
||||
*/
|
||||
class epicsShareClass ConfigurationProvider : private epics::pvData::NoDefaultMethods
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ConfigurationProvider);
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~ConfigurationProvider() {};
|
||||
/**
|
||||
* Return configuration specified by name.
|
||||
*
|
||||
* @param name name of the configuration to return.
|
||||
*
|
||||
* @return configuration specified by name or NULL if it does not exists.
|
||||
*/
|
||||
virtual Configuration::shared_pointer getConfiguration(const std::string &name) = 0;
|
||||
/**
|
||||
* Register configuration.
|
||||
*
|
||||
* @param name name of the configuration to register.
|
||||
* @param configuration configuration to register.
|
||||
*/
|
||||
virtual void registerConfiguration(const std::string &name, Configuration::shared_pointer const & configuration) = 0;
|
||||
};
|
||||
|
||||
class ConfigurationProviderImpl: public ConfigurationProvider
|
||||
{
|
||||
public:
|
||||
ConfigurationProviderImpl();
|
||||
/**
|
||||
* Destructor. Note: Registered configurations will be deleted!!
|
||||
*/
|
||||
~ConfigurationProviderImpl();
|
||||
Configuration::shared_pointer getConfiguration(const std::string &name);
|
||||
void registerConfiguration(const std::string &name, Configuration::shared_pointer const & configuration);
|
||||
private:
|
||||
epics::pvData::Mutex _mutex;
|
||||
std::map<std::string,Configuration::shared_pointer> _configs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configuration factory.
|
||||
*/
|
||||
class epicsShareClass ConfigurationFactory : private epics::pvData::NoDefaultMethods
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ConfigurationFactory);
|
||||
|
||||
/**
|
||||
* Lazily creates configuration provider.
|
||||
*
|
||||
* @param name name of the configuration to register.
|
||||
* @param configuration configuration to register.
|
||||
*
|
||||
* @return configuration provider
|
||||
*/
|
||||
static ConfigurationProvider::shared_pointer getProvider();
|
||||
|
||||
private:
|
||||
ConfigurationFactory() {};
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* CONFIGURATION_H */
|
||||
106
src/utils/hexDump.cpp
Normal file
106
src/utils/hexDump.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* 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 <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/hexDump.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
using std::stringstream;
|
||||
using std::endl;
|
||||
using std::cout;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
/// Byte to hexchar mapping.
|
||||
static const char lookup[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
|
||||
/// Get hex representation of byte.
|
||||
String toHex(int8 b) {
|
||||
String sb;
|
||||
|
||||
int upper = (b>>4)&0x0F;
|
||||
sb += lookup[upper];
|
||||
|
||||
int lower = b&0x0F;
|
||||
sb += lookup[lower];
|
||||
|
||||
sb += ' ';
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
/// Get ASCII representation of byte, dot if non-readable.
|
||||
char toAscii(int8 b) {
|
||||
if(b>(int8)31&&b<(int8)127)
|
||||
return (char)b;
|
||||
else
|
||||
return '.';
|
||||
}
|
||||
|
||||
void hexDump(String const & name, const int8 *bs, int len) {
|
||||
hexDump(name, bs, 0, len);
|
||||
}
|
||||
|
||||
void hexDump(String const & name, const int8 *bs, int start, int len) {
|
||||
hexDump("", name, bs, start, len);
|
||||
}
|
||||
|
||||
void hexDump(String const & prologue, String const & name, const int8 *bs,
|
||||
int start, int len) {
|
||||
|
||||
stringstream header;
|
||||
|
||||
header<<prologue<<endl<<"Hexdump ["<<name<<"] size = "<<len;
|
||||
|
||||
String out(header.str());
|
||||
|
||||
String chars;
|
||||
|
||||
for(int i = start; i<(start+len); i++) {
|
||||
if(((i-start)%16)==0) {
|
||||
out += chars;
|
||||
out += '\n';
|
||||
chars.erase();
|
||||
}
|
||||
|
||||
chars += toAscii(bs[i]);
|
||||
|
||||
out += toHex(bs[i]);
|
||||
|
||||
if(((i-start)%4)==3) {
|
||||
chars += ' ';
|
||||
out += ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if(len%16!=0) {
|
||||
int pad = 0;
|
||||
int delta_bytes = 16-(len%16);
|
||||
|
||||
//rest of line (no of bytes)
|
||||
//each byte takes two chars plus one ws
|
||||
pad = delta_bytes*3;
|
||||
|
||||
//additional whitespaces after four bytes
|
||||
pad += (delta_bytes/4);
|
||||
pad++;
|
||||
|
||||
for(int i = 0; i<pad; i++)
|
||||
chars.insert(0, " ");
|
||||
}
|
||||
|
||||
out += chars;
|
||||
cout<<out<<endl;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
58
src/utils/hexDump.h
Normal file
58
src/utils/hexDump.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef HEXDUMP_H_
|
||||
#define HEXDUMP_H_
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define hexDumpEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvType.h>
|
||||
|
||||
#ifdef hexDumpEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef hexDumpEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
/**
|
||||
* Output a buffer in hex format.
|
||||
* @param name name (description) of the message.
|
||||
* @param bs buffer to dump
|
||||
* @param len first bytes (length) to dump.
|
||||
*/
|
||||
epicsShareFunc void hexDump(epics::pvData::String const & name, const epics::pvData::int8 *bs, int len);
|
||||
|
||||
/**
|
||||
* Output a buffer in hex format.
|
||||
* @param[in] name name (description) of the message.
|
||||
* @param[in] bs buffer to dump
|
||||
* @param[in] start dump message using given offset.
|
||||
* @param[in] len first bytes (length) to dump.
|
||||
*/
|
||||
epicsShareFunc void hexDump(epics::pvData::String const & name, const epics::pvData::int8 *bs, int start, int len);
|
||||
|
||||
/**
|
||||
* Output a buffer in hex format.
|
||||
* @param[in] prologue string to prefixed to debug output, can be <code>null</code>
|
||||
* @param[in] name name (description) of the message.
|
||||
* @param[in] bs buffer to dump
|
||||
* @param[in] start dump message using given offset.
|
||||
* @param[in] len first bytes (length) to dump.
|
||||
*/
|
||||
epicsShareFunc void hexDump(epics::pvData::String const & prologue, epics::pvData::String const & name,
|
||||
const epics::pvData::int8 *bs, int start, int len);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HEXDUMP_H_ */
|
||||
160
src/utils/inetAddressUtil.cpp
Normal file
160
src/utils/inetAddressUtil.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* 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 <vector>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/epicsException.h>
|
||||
#include <osiSock.h>
|
||||
#include <ellLib.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/inetAddressUtil.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
void addDefaultBroadcastAddress(InetAddrVector* v, unsigned short p) {
|
||||
osiSockAddr pNewNode;
|
||||
pNewNode.ia.sin_family = AF_INET;
|
||||
pNewNode.ia.sin_addr.s_addr = htonl(INADDR_BROADCAST);
|
||||
pNewNode.ia.sin_port = htons(p);
|
||||
v->push_back(pNewNode);
|
||||
}
|
||||
|
||||
InetAddrVector* getBroadcastAddresses(SOCKET sock,
|
||||
unsigned short defaultPort) {
|
||||
ELLLIST as;
|
||||
ellInit(&as);
|
||||
osiSockAddr serverAddr;
|
||||
memset(&serverAddr, 0, sizeof(osiSockAddr));
|
||||
InetAddrVector * v = new InetAddrVector;
|
||||
osiSockDiscoverBroadcastAddresses(&as, sock, &serverAddr);
|
||||
for(ELLNODE * n = ellFirst(&as); n != NULL; n = ellNext(n))
|
||||
{
|
||||
osiSockAddrNode * sn = (osiSockAddrNode *)n;
|
||||
sn->addr.ia.sin_port = htons(defaultPort);
|
||||
v->push_back(sn->addr);
|
||||
}
|
||||
ellFree(&as);
|
||||
return v;
|
||||
}
|
||||
|
||||
void encodeAsIPv6Address(ByteBuffer* buffer, const osiSockAddr* address) {
|
||||
// IPv4 compatible IPv6 address
|
||||
// first 80-bit are 0
|
||||
buffer->putLong(0);
|
||||
buffer->putShort(0);
|
||||
// next 16-bits are 1
|
||||
buffer->putShort(0xFFFF);
|
||||
// following IPv4 address in big-endian (network) byte order
|
||||
uint32_t ipv4Addr = ntohl(address->ia.sin_addr.s_addr);
|
||||
buffer->putByte((int8)((ipv4Addr>>24)&0xFF));
|
||||
buffer->putByte((int8)((ipv4Addr>>16)&0xFF));
|
||||
buffer->putByte((int8)((ipv4Addr>>8)&0xFF));
|
||||
buffer->putByte((int8)(ipv4Addr&0xFF));
|
||||
}
|
||||
|
||||
osiSockAddr* intToIPv4Address(int32 addr) {
|
||||
osiSockAddr* ret = new osiSockAddr;
|
||||
ret->ia.sin_family = AF_INET;
|
||||
ret->ia.sin_addr.s_addr = htonl(addr);
|
||||
ret->ia.sin_port = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32 ipv4AddressToInt(const osiSockAddr& addr) {
|
||||
return (int32)ntohl(addr.ia.sin_addr.s_addr);
|
||||
}
|
||||
|
||||
int32 parseInetAddress(const String addr) {
|
||||
int32 retAddr;
|
||||
|
||||
size_t dot = addr.find('.');
|
||||
if(dot==String::npos) THROW_BASE_EXCEPTION("Not an IPv4 address.");
|
||||
int byte = atoi(addr.substr(0, dot).c_str());
|
||||
if(byte<0||byte>255) THROW_BASE_EXCEPTION("Not an IPv4 address.");
|
||||
retAddr = byte;
|
||||
|
||||
int num = dot+1;
|
||||
dot = addr.find('.', num);
|
||||
if(dot==String::npos) THROW_BASE_EXCEPTION("Not an IPv4 address.");
|
||||
byte = atoi(addr.substr(num, dot-num).c_str());
|
||||
if(byte<0||byte>255) THROW_BASE_EXCEPTION("Not an IPv4 address.");
|
||||
retAddr <<= 8;
|
||||
retAddr |= byte;
|
||||
|
||||
num = dot+1;
|
||||
dot = addr.find('.', num);
|
||||
if(dot==String::npos) THROW_BASE_EXCEPTION("Not an IPv4 address.");
|
||||
byte = atoi(addr.substr(num, dot-num).c_str());
|
||||
if(byte<0||byte>255) THROW_BASE_EXCEPTION("Not an IPv4 address.");
|
||||
retAddr <<= 8;
|
||||
retAddr |= byte;
|
||||
|
||||
num = dot+1;
|
||||
byte = atoi(addr.substr(num).c_str());
|
||||
if(byte<0||byte>255) THROW_BASE_EXCEPTION("Not an IPv4 address.");
|
||||
retAddr <<= 8;
|
||||
retAddr |= byte;
|
||||
|
||||
return htonl(retAddr);
|
||||
}
|
||||
|
||||
InetAddrVector* getSocketAddressList(String list, int defaultPort,
|
||||
const InetAddrVector* appendList) {
|
||||
InetAddrVector* iav = new InetAddrVector();
|
||||
|
||||
// parse string
|
||||
size_t subStart = 0;
|
||||
size_t subEnd;
|
||||
while((subEnd = list.find(' ', subStart))!=String::npos) {
|
||||
String address = list.substr(subStart, (subEnd-subStart));
|
||||
osiSockAddr addr;
|
||||
aToIPAddr(address.c_str(), defaultPort, &addr.ia);
|
||||
iav->push_back(addr);
|
||||
subStart = list.find_first_not_of(" \t\r\n\v", subEnd);
|
||||
}
|
||||
|
||||
if(subStart!=String::npos&&list.length()>0) {
|
||||
osiSockAddr addr;
|
||||
aToIPAddr(list.substr(subStart).c_str(), defaultPort, &addr.ia);
|
||||
iav->push_back(addr);
|
||||
}
|
||||
|
||||
if(appendList!=NULL) {
|
||||
for(size_t i = 0; i<appendList->size(); i++)
|
||||
iav->push_back((*appendList)[i]);
|
||||
}
|
||||
return iav;
|
||||
}
|
||||
|
||||
const String inetAddressToString(const osiSockAddr &addr,
|
||||
bool displayPort, bool displayHex) {
|
||||
stringstream saddr;
|
||||
|
||||
int ipa = ntohl(addr.ia.sin_addr.s_addr);
|
||||
|
||||
saddr<<((int)(ipa>>24)&0xFF)<<'.';
|
||||
saddr<<((int)(ipa>>16)&0xFF)<<'.';
|
||||
saddr<<((int)(ipa>>8)&0xFF)<<'.';
|
||||
saddr<<((int)ipa&0xFF);
|
||||
if(displayPort) saddr<<":"<<ntohs(addr.ia.sin_port);
|
||||
if(displayHex) saddr<<" ("<<hex<<ntohl(addr.ia.sin_addr.s_addr)
|
||||
<<")";
|
||||
|
||||
return saddr.str();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
123
src/utils/inetAddressUtil.h
Normal file
123
src/utils/inetAddressUtil.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef INETADDRESSUTIL_H_
|
||||
#define INETADDRESSUTIL_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define inetAddressUtilExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
|
||||
#include <osiSock.h>
|
||||
|
||||
#ifdef inetAddressUtilExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef inetAddressUtilExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
|
||||
// TODO implement using smart pointers
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
typedef std::vector<osiSockAddr> InetAddrVector;
|
||||
|
||||
/**
|
||||
* Returns a vector containing all the IPv4 broadcast addresses on this machine.
|
||||
* IPv6 doesn't have a local broadcast address.
|
||||
* Conversion of the defaultPort to network byte order performed by
|
||||
* the function.
|
||||
*/
|
||||
epicsShareFunc InetAddrVector* getBroadcastAddresses(SOCKET sock, unsigned short defaultPort);
|
||||
|
||||
/**
|
||||
* Encode IPv4 address as IPv6 address.
|
||||
* @param buffer byte-buffer where to put encoded data.
|
||||
* @param address address to encode.
|
||||
*/
|
||||
epicsShareFunc void encodeAsIPv6Address(epics::pvData::ByteBuffer* buffer, const osiSockAddr* address);
|
||||
|
||||
/**
|
||||
* Convert an integer into an IPv4 INET address.
|
||||
* @param addr integer representation of a given address.
|
||||
* @return IPv4 INET address.
|
||||
*/
|
||||
epicsShareFunc osiSockAddr* intToIPv4Address(epics::pvData::int32 addr);
|
||||
|
||||
/**
|
||||
* Convert an IPv4 INET address to an integer.
|
||||
* @param addr IPv4 INET address.
|
||||
* @return integer representation of a given address.
|
||||
*/
|
||||
epicsShareFunc epics::pvData::int32 ipv4AddressToInt(const osiSockAddr& addr);
|
||||
|
||||
/**
|
||||
* Parse space delimited addresss[:port] string and return array of <code>InetSocketAddress</code>.
|
||||
* @param list space delimited addresss[:port] string.
|
||||
* @param defaultPort port take if not specified.
|
||||
* @param appendList list to be appended.
|
||||
* @return array of <code>InetSocketAddress</code>.
|
||||
*/
|
||||
epicsShareFunc InetAddrVector* getSocketAddressList(epics::pvData::String list, int defaultPort,
|
||||
const InetAddrVector* appendList = NULL);
|
||||
|
||||
epicsShareFunc const epics::pvData::String inetAddressToString(const osiSockAddr &addr,
|
||||
bool displayPort = true, bool displayHex = false);
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
// comparators for osiSockAddr
|
||||
|
||||
struct comp_osiSockAddrPtr {
|
||||
bool operator()(osiSockAddr const *a, osiSockAddr const *b) const {
|
||||
if(a->sa.sa_family<b->sa.sa_family) return true;
|
||||
if((a->sa.sa_family==b->sa.sa_family)&&(a->ia.sin_addr.s_addr
|
||||
<b->ia.sin_addr.s_addr)) return true;
|
||||
if((a->sa.sa_family==b->sa.sa_family)&&(a->ia.sin_addr.s_addr
|
||||
==b->ia.sin_addr.s_addr)&&(a->ia.sin_port
|
||||
<b->ia.sin_port)) return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct comp_osiSock_lt {
|
||||
bool operator()(const osiSockAddr& a, const osiSockAddr& b) const {
|
||||
if(a.sa.sa_family<b.sa.sa_family) return true;
|
||||
if((a.sa.sa_family==b.sa.sa_family)&&(a.ia.sin_addr.s_addr
|
||||
<b.ia.sin_addr.s_addr)) return true;
|
||||
if((a.sa.sa_family==b.sa.sa_family)&&(a.ia.sin_addr.s_addr
|
||||
==b.ia.sin_addr.s_addr)&&(a.ia.sin_port
|
||||
<b.ia.sin_port)) return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//TODO if unordered map is used instead of map we can use sockAddrAreIdentical routine from osiSock.h
|
||||
struct comp_osiSockAddr {
|
||||
bool operator()(osiSockAddr const a, osiSockAddr const b) const {
|
||||
if(a.sa.sa_family<b.sa.sa_family) return true;
|
||||
if((a.sa.sa_family==b.sa.sa_family)&&(a.ia.sin_addr.s_addr
|
||||
<b.ia.sin_addr.s_addr)) return true;
|
||||
if((a.sa.sa_family==b.sa.sa_family)&&(a.ia.sin_addr.s_addr
|
||||
==b.ia.sin_addr.s_addr)&&(a.ia.sin_port
|
||||
<b.ia.sin_port)) return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* INETADDRESSUTIL_H_ */
|
||||
166
src/utils/introspectionRegistry.cpp
Normal file
166
src/utils/introspectionRegistry.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* 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/introspectionRegistry.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/serializationHelper.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace std;
|
||||
using std::tr1::static_pointer_cast;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
const int8 IntrospectionRegistry::NULL_TYPE_CODE = (int8)-1;
|
||||
const int8 IntrospectionRegistry::ONLY_ID_TYPE_CODE = (int8)-2;
|
||||
const int8 IntrospectionRegistry::FULL_WITH_ID_TYPE_CODE = (int8)-3;
|
||||
FieldCreatePtr IntrospectionRegistry::_fieldCreate(getFieldCreate());
|
||||
|
||||
IntrospectionRegistry::IntrospectionRegistry()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
IntrospectionRegistry::~IntrospectionRegistry()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void IntrospectionRegistry::reset()
|
||||
{
|
||||
_pointer = 1;
|
||||
_registry.clear();
|
||||
}
|
||||
|
||||
FieldConstPtr IntrospectionRegistry::getIntrospectionInterface(const int16 id)
|
||||
{
|
||||
registryMap_t::iterator registryIter = _registry.find(id);
|
||||
if(registryIter == _registry.end())
|
||||
{
|
||||
return FieldConstPtr();
|
||||
}
|
||||
return registryIter->second;
|
||||
}
|
||||
|
||||
void IntrospectionRegistry::registerIntrospectionInterface(const int16 id, FieldConstPtr const & field)
|
||||
{
|
||||
_registry[id] = field;
|
||||
}
|
||||
|
||||
int16 IntrospectionRegistry::registerIntrospectionInterface(FieldConstPtr const & field, bool& existing)
|
||||
{
|
||||
int16 key;
|
||||
// TODO this is slow
|
||||
if(registryContainsValue(field, key))
|
||||
{
|
||||
existing = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
existing = false;
|
||||
key = _pointer++;
|
||||
_registry[key] = field;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
void IntrospectionRegistry::printKeysAndValues(string name)
|
||||
{
|
||||
string buffer;
|
||||
cout << "############## print of all key/values of " << name.c_str() << " registry : ###################" << endl;
|
||||
for(registryMap_t::iterator registryIter = _registry.begin(); registryIter != _registry.end(); registryIter++)
|
||||
{
|
||||
buffer.erase();
|
||||
cout << "\t" << "Key: "<< registryIter->first << endl;
|
||||
cout << "\t" << "Value: " << registryIter->second << endl;
|
||||
|
||||
cout << "\t" << "References: " << buffer.c_str() << endl;
|
||||
buffer.erase();
|
||||
registryIter->second->toString(&buffer);
|
||||
cout << "\t" << "Value toString: " << buffer.c_str() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO slow !!!!
|
||||
bool IntrospectionRegistry::registryContainsValue(FieldConstPtr const & field, int16& key)
|
||||
{
|
||||
for(registryMap_t::reverse_iterator registryRIter = _registry.rbegin(); registryRIter != _registry.rend(); registryRIter++)
|
||||
{
|
||||
if(*(field.get()) == *(registryRIter->second))
|
||||
{
|
||||
key = registryRIter->first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void IntrospectionRegistry::serialize(FieldConstPtr const & field, ByteBuffer* buffer, SerializableControl* control)
|
||||
{
|
||||
if (field.get() == NULL)
|
||||
{
|
||||
SerializationHelper::serializeNullField(buffer, control);
|
||||
}
|
||||
else
|
||||
{
|
||||
// do not cache scalars, scalarArrays
|
||||
// ... and (array of) variant unions - not worth the complex condition,
|
||||
// unless bool Field.cache() would exist
|
||||
if (field->getType() != scalar &&
|
||||
field->getType() != scalarArray)
|
||||
{
|
||||
bool existing;
|
||||
const int16 key = registerIntrospectionInterface(field, existing);
|
||||
if (existing) {
|
||||
control->ensureBuffer(3);
|
||||
buffer->putByte(ONLY_ID_TYPE_CODE);
|
||||
buffer->putShort(key);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
control->ensureBuffer(3);
|
||||
buffer->putByte(FULL_WITH_ID_TYPE_CODE); // could also be a mask
|
||||
buffer->putShort(key);
|
||||
}
|
||||
}
|
||||
|
||||
field->serialize(buffer, control);
|
||||
}
|
||||
}
|
||||
|
||||
FieldConstPtr IntrospectionRegistry::deserialize(ByteBuffer* buffer, DeserializableControl* control)
|
||||
{
|
||||
control->ensureData(1);
|
||||
size_t pos = buffer->getPosition();
|
||||
const int8 typeCode = buffer->getByte();
|
||||
|
||||
if (typeCode == NULL_TYPE_CODE)
|
||||
{
|
||||
return FieldConstPtr();
|
||||
}
|
||||
else if (typeCode == ONLY_ID_TYPE_CODE)
|
||||
{
|
||||
control->ensureData(sizeof(int16)/sizeof(int8));
|
||||
return getIntrospectionInterface(buffer->getShort());
|
||||
}
|
||||
// could also be a mask
|
||||
if(typeCode == IntrospectionRegistry::FULL_WITH_ID_TYPE_CODE)
|
||||
{
|
||||
control->ensureData(sizeof(int16)/sizeof(int8));
|
||||
const short key = buffer->getShort();
|
||||
FieldConstPtr field = _fieldCreate->deserialize(buffer, control);
|
||||
registerIntrospectionInterface(key, field);
|
||||
return field;
|
||||
}
|
||||
|
||||
// return typeCode back
|
||||
buffer->setPosition(pos);
|
||||
return _fieldCreate->deserialize(buffer, control);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
134
src/utils/introspectionRegistry.h
Normal file
134
src/utils/introspectionRegistry.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef INTROSPECTIONREGISTRY_H
|
||||
#define INTROSPECTIONREGISTRY_H
|
||||
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define introspectionRegistryEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
#include <pv/serialize.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
#include <pv/status.h>
|
||||
#include <pv/standardField.h>
|
||||
|
||||
#ifdef introspectionRegistryEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef introspectionRegistryEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
// TODO check for memory leaks
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
typedef std::map<const short,epics::pvData::FieldConstPtr> registryMap_t;
|
||||
|
||||
|
||||
/**
|
||||
* PVData Structure registry.
|
||||
* Registry is used to cache introspection interfaces to minimize network traffic.
|
||||
* @author gjansa
|
||||
*/
|
||||
class IntrospectionRegistry : public epics::pvData::NoDefaultMethods {
|
||||
public:
|
||||
IntrospectionRegistry();
|
||||
virtual ~IntrospectionRegistry();
|
||||
|
||||
void printKeysAndValues(std::string name);
|
||||
/**
|
||||
* Resets registry, i.e. must be done when transport is changed (server restarted).
|
||||
*/
|
||||
void reset();
|
||||
/**
|
||||
* Get introspection interface for given ID.
|
||||
*
|
||||
* @param id id of the introspection interface to get
|
||||
*
|
||||
* @return <code>Field</code> instance for given ID.
|
||||
*/
|
||||
epics::pvData::FieldConstPtr getIntrospectionInterface(const epics::pvData::int16 id);
|
||||
|
||||
/**
|
||||
* Registers introspection interface with given ID. Always INCOMING.
|
||||
*
|
||||
* @param id id of the introspection interface to register
|
||||
* @param field introspection interface to register
|
||||
*/
|
||||
void registerIntrospectionInterface(const epics::pvData::int16 id, epics::pvData::FieldConstPtr const & field);
|
||||
|
||||
/**
|
||||
* Registers introspection interface and get it's ID. Always OUTGOING.
|
||||
* If it is already registered only preassigned ID is returned.
|
||||
*
|
||||
* TODO !!!!!!this can get very slow in large maps. We need to change this !!!!!!
|
||||
*
|
||||
* @param field introspection interface to register
|
||||
*
|
||||
* @return id of given introspection interface
|
||||
*/
|
||||
epics::pvData::int16 registerIntrospectionInterface(epics::pvData::FieldConstPtr const & field, bool& existing);
|
||||
|
||||
/**
|
||||
* Serializes introspection interface
|
||||
*
|
||||
* @param field
|
||||
* @param buffer
|
||||
* @param control
|
||||
*/
|
||||
void serialize(epics::pvData::FieldConstPtr const & field, epics::pvData::ByteBuffer* buffer, epics::pvData::SerializableControl* control);
|
||||
|
||||
/**
|
||||
* Deserializes introspection interface
|
||||
*
|
||||
* TODO
|
||||
*
|
||||
* @param buffer
|
||||
* @param control
|
||||
*
|
||||
* @return <code>Field</code> deserialized from the buffer.
|
||||
*/
|
||||
epics::pvData::FieldConstPtr deserialize(epics::pvData::ByteBuffer* buffer, epics::pvData::DeserializableControl* control);
|
||||
|
||||
/**
|
||||
* Null type.
|
||||
*/
|
||||
const static epics::pvData::int8 NULL_TYPE_CODE;
|
||||
|
||||
/**
|
||||
* Serialization contains only an ID (that was assigned by one of the previous <code>FULL_WITH_ID</code> descriptions).
|
||||
*/
|
||||
const static epics::pvData::int8 ONLY_ID_TYPE_CODE;
|
||||
|
||||
/**
|
||||
* Serialization contains an ID (that can be used later, if cached) and full interface description.
|
||||
*/
|
||||
const static epics::pvData::int8 FULL_WITH_ID_TYPE_CODE;
|
||||
|
||||
private:
|
||||
registryMap_t _registry;
|
||||
epics::pvData::int16 _pointer;
|
||||
|
||||
/**
|
||||
* Field factory.
|
||||
*/
|
||||
static epics::pvData::FieldCreatePtr _fieldCreate;
|
||||
|
||||
bool registryContainsValue(epics::pvData::FieldConstPtr const & field, epics::pvData::int16& key);
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* INTROSPECTIONREGISTRY_H */
|
||||
18
src/utils/likely.h
Normal file
18
src/utils/likely.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIKELY_H_
|
||||
#define LIKELY_H_
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 3
|
||||
#define likely(x) __builtin_expect (x, 1)
|
||||
#define unlikely(x) __builtin_expect (x, 0)
|
||||
#else
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
|
||||
#endif /* LIKELY_H_ */
|
||||
116
src/utils/logger.cpp
Normal file
116
src/utils/logger.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* 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 <fstream>
|
||||
#include <iostream>
|
||||
#include <time.h>
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <pv/noDefaultMethods.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvType.h>
|
||||
|
||||
#include <epicsExit.h>
|
||||
#include <errlog.h>
|
||||
|
||||
#include <epicsTime.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/logger.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
using std::ofstream;
|
||||
using std::ios;
|
||||
using std::endl;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
#define TIMETEXTLEN 32
|
||||
|
||||
static pvAccessLogLevel g_pvAccessLogLevel = logLevelInfo;
|
||||
|
||||
void pvAccessLog(pvAccessLogLevel level, const char* format, ...)
|
||||
{
|
||||
// TODO lock
|
||||
if (level >= g_pvAccessLogLevel)
|
||||
{
|
||||
char timeText[TIMETEXTLEN];
|
||||
epicsTimeStamp tsNow;
|
||||
|
||||
epicsTimeGetCurrent(&tsNow);
|
||||
epicsTimeToStrftime(timeText, TIMETEXTLEN, "%Y-%m-%dT%H:%M:%S.%03f", &tsNow);
|
||||
|
||||
printf("%s ", timeText);
|
||||
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
vprintf(format, arg);
|
||||
va_end(arg);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void pvAccessSetLogLevel(pvAccessLogLevel level)
|
||||
{
|
||||
g_pvAccessLogLevel = level;
|
||||
}
|
||||
|
||||
bool pvAccessIsLoggable(pvAccessLogLevel level)
|
||||
{
|
||||
return level >= g_pvAccessLogLevel;
|
||||
}
|
||||
|
||||
class FileLogger : public NoDefaultMethods {
|
||||
public:
|
||||
FileLogger(String const & name) {
|
||||
logFile.open(name.data(), ios::app);
|
||||
}
|
||||
|
||||
~FileLogger() {
|
||||
logFile.close();
|
||||
}
|
||||
|
||||
void logMessage(const char* message) {
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
char* timeStr = ctime(&rawtime);
|
||||
timeStr[strlen(timeStr)-1]='\0'; // remove newline
|
||||
|
||||
logFile<<timeStr<<"\t"<<message; // the newline is added by the caller
|
||||
}
|
||||
private:
|
||||
ofstream logFile;
|
||||
|
||||
};
|
||||
|
||||
static FileLogger* fileLogger = NULL;
|
||||
|
||||
static void errLogFileListener(void* /*pPrivate*/, const char *message) {
|
||||
fileLogger->logMessage(message);
|
||||
}
|
||||
|
||||
static void exitFileLoggerHandler(void* /*pPrivate*/) {
|
||||
errlogFlush();
|
||||
delete fileLogger;
|
||||
}
|
||||
|
||||
void createFileLogger(String const & fname) {
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
|
||||
if(fileLogger==NULL) {
|
||||
fileLogger = new FileLogger(fname);
|
||||
errlogInit(2048);
|
||||
errlogAddListener(errLogFileListener, NULL);
|
||||
epicsAtExit(exitFileLoggerHandler, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
83
src/utils/logger.h
Normal file
83
src/utils/logger.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LOGGER_H_
|
||||
#define LOGGER_H_
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define loggerEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/pvType.h>
|
||||
#include <errlog.h>
|
||||
|
||||
#ifdef loggerEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
#undef loggerEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
typedef enum { logLevelAll = 0, logLevelTrace, logLevelDebug, logLevelInfo,
|
||||
logLevelWarn, logLevelError, logLevelFatal, logLevelOff } pvAccessLogLevel;
|
||||
/*
|
||||
ALL
|
||||
The ALL has the lowest possible rank and is intended to turn on all logging.
|
||||
TRACE
|
||||
The TRACE Level designates finer-grained informational events than the DEBUG
|
||||
DEBUG
|
||||
The DEBUG Level designates fine-grained informational events that are most useful to debug an application.
|
||||
INFO
|
||||
The INFO level designates informational messages that highlight the progress of the application at coarse-grained level.
|
||||
WARN
|
||||
The WARN level designates potentially harmful situations.
|
||||
ERROR
|
||||
The ERROR level designates error events that might still allow the application to continue running.
|
||||
FATAL
|
||||
The FATAL level designates very severe error events that will presumably lead the application to abort.
|
||||
OFF
|
||||
The OFF has the highest possible rank and is intended to turn off logging.
|
||||
*/
|
||||
|
||||
|
||||
epicsShareExtern void pvAccessLog(pvAccessLogLevel level, const char* format, ...);
|
||||
epicsShareExtern void pvAccessSetLogLevel(pvAccessLogLevel level);
|
||||
epicsShareExtern bool pvAccessIsLoggable(pvAccessLogLevel level);
|
||||
|
||||
#if defined (__GNUC__) && __GNUC__ < 3
|
||||
#define LOG(level, format, ARGS...) pvAccessLog(level, format, ##ARGS)
|
||||
#else
|
||||
#define LOG(level, format, ...) pvAccessLog(level, format, ##__VA_ARGS__)
|
||||
#endif
|
||||
#define SET_LOG_LEVEL(level) pvAccessSetLogLevel(level)
|
||||
#define IS_LOGGABLE(level) pvAccessIsLoggable(level)
|
||||
|
||||
// EPICS errlog
|
||||
//#define LOG errlogSevPrintf
|
||||
//#define SET_LOG_LEVEL(level) errlogSetSevToLog(level)
|
||||
|
||||
// none
|
||||
//#define LOG(level, fmt, ...)
|
||||
//#define SET_LOG_LEVEL(level)
|
||||
|
||||
/**
|
||||
* Create a logger that will write to file indicated by the <tt>fname</tt>.
|
||||
* After creation you are free to use standard EPICSv3 functions from
|
||||
* <tt>errlog.h</tt>.
|
||||
*
|
||||
* @param[in] fname The file to write to. If the file exists, it
|
||||
* is opened for append.
|
||||
*/
|
||||
epicsShareExtern void createFileLogger( epics::pvData::String const & fname );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LOGGER_H_ */
|
||||
161
src/utils/namedLockPattern.h
Normal file
161
src/utils/namedLockPattern.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef NAMEDLOCKPATTERN_H
|
||||
#define NAMEDLOCKPATTERN_H
|
||||
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define namedLockPatternEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#ifdef namedLockPatternEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef namedLockPatternEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/referenceCountingLock.h>
|
||||
|
||||
// TODO implement using smart pointers
|
||||
|
||||
namespace epics { namespace pvAccess {
|
||||
|
||||
/**
|
||||
* NamedLockPattern
|
||||
*/
|
||||
template <class Key, class Compare = std::less<Key> >
|
||||
class NamedLockPattern
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
NamedLockPattern() {};
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~NamedLockPattern() {};
|
||||
/**
|
||||
* Acquire synchronization lock for named object.
|
||||
*
|
||||
* NOTE: Argument msecs is currently not supported due to
|
||||
* Darwin OS not supporting pthread_mutex_timedlock. May be changed in the future.
|
||||
*
|
||||
* @param name name of the object whose lock to acquire.
|
||||
* @param msec the number of milleseconds to wait.
|
||||
* An argument less than or equal to zero means not to wait at all.
|
||||
* @return <code>true</code> if acquired, <code>false</code> othwerwise.
|
||||
* NOTE: currently this routine always returns true. Look above for explanation.
|
||||
*/
|
||||
bool acquireSynchronizationObject(const Key& name, const epics::pvData::int64 msec);
|
||||
/**
|
||||
* Release synchronization lock for named object.
|
||||
* @param name name of the object whose lock to release.
|
||||
*/
|
||||
void releaseSynchronizationObject(const Key& name);
|
||||
private:
|
||||
epics::pvData::Mutex _mutex;
|
||||
std::map<const Key,ReferenceCountingLock::shared_pointer,Compare> _namedLocks;
|
||||
typename std::map<const Key,ReferenceCountingLock::shared_pointer,Compare>::iterator _namedLocksIter;
|
||||
|
||||
/**
|
||||
* Release synchronization lock for named object.
|
||||
* @param name name of the object whose lock to release.
|
||||
* @param release set to <code>false</code> if there is no need to call release
|
||||
* on synchronization lock.
|
||||
*/
|
||||
void releaseSynchronizationObject(const Key& name,const bool release);
|
||||
};
|
||||
|
||||
template <class Key, class Compare>
|
||||
bool NamedLockPattern<Key,Compare>::acquireSynchronizationObject(const Key& name, const epics::pvData::int64 msec)
|
||||
{
|
||||
ReferenceCountingLock::shared_pointer lock;
|
||||
{ //due to guard
|
||||
epics::pvData::Lock guard(_mutex);
|
||||
|
||||
_namedLocksIter = _namedLocks.find(name);
|
||||
// get synchronization object
|
||||
|
||||
// none is found, create and return new one
|
||||
// increment references
|
||||
if(_namedLocksIter == _namedLocks.end())
|
||||
{
|
||||
lock.reset(new ReferenceCountingLock());
|
||||
_namedLocks[name] = lock;
|
||||
}
|
||||
else
|
||||
{
|
||||
lock = _namedLocksIter->second;
|
||||
lock->increment();
|
||||
}
|
||||
} // end of guarded area
|
||||
|
||||
bool success = lock->acquire(msec);
|
||||
|
||||
if(!success)
|
||||
{
|
||||
releaseSynchronizationObject(name, false);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
template <class Key, class Compare>
|
||||
void NamedLockPattern<Key,Compare>::releaseSynchronizationObject(const Key& name)
|
||||
{
|
||||
releaseSynchronizationObject(name, true);
|
||||
}
|
||||
|
||||
template <class Key, class Compare>
|
||||
void NamedLockPattern<Key,Compare>::releaseSynchronizationObject(const Key& name,const bool release)
|
||||
{
|
||||
epics::pvData::Lock guard(_mutex);
|
||||
ReferenceCountingLock::shared_pointer lock;
|
||||
_namedLocksIter = _namedLocks.find(name);
|
||||
|
||||
// release lock
|
||||
if (_namedLocksIter != _namedLocks.end())
|
||||
{
|
||||
lock = _namedLocksIter->second;
|
||||
|
||||
// release the lock
|
||||
if (release)
|
||||
{
|
||||
lock->release();
|
||||
}
|
||||
|
||||
// if there only one current lock exists
|
||||
// remove it from the map
|
||||
if (lock->decrement() <= 0)
|
||||
{
|
||||
_namedLocks.erase(_namedLocksIter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Key, class Compare>
|
||||
class NamedLock : private epics::pvData::NoDefaultMethods
|
||||
{
|
||||
public:
|
||||
NamedLock(NamedLockPattern<Key,Compare>* namedLockPattern): _namedLockPattern(namedLockPattern) {}
|
||||
bool acquireSynchronizationObject(const Key& name, const epics::pvData::int64 msec) {_name = name; return _namedLockPattern->acquireSynchronizationObject(name,msec);}
|
||||
~NamedLock(){_namedLockPattern->releaseSynchronizationObject(_name);}
|
||||
private:
|
||||
Key _name;
|
||||
NamedLockPattern<Key,Compare>* _namedLockPattern;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* NAMEDLOCKPATTERN_H */
|
||||
99
src/utils/referenceCountingLock.cpp
Normal file
99
src/utils/referenceCountingLock.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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/referenceCountingLock.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
ReferenceCountingLock::ReferenceCountingLock(): _references(1)
|
||||
{
|
||||
/* pthread_mutexattr_t mutexAttribute;
|
||||
int32 retval = pthread_mutexattr_init(&mutexAttribute);
|
||||
if(retval != 0)
|
||||
{
|
||||
//string errMsg = "Error: pthread_mutexattr_init failed: " + string(strerror(retval));
|
||||
assert(false);
|
||||
}
|
||||
retval = pthread_mutexattr_settype(&mutexAttribute, PTHREAD_MUTEX_RECURSIVE);
|
||||
if(retval == 0)
|
||||
{
|
||||
retval = pthread_mutex_init(_mutex, &mutexAttribute);
|
||||
if(retval != 0)
|
||||
{
|
||||
//string errMsg = "Error: pthread_mutex_init failed: " + string(strerror(retval));
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//string errMsg = "Error: pthread_mutexattr_settype failed: " + string(strerror(retval));
|
||||
assert(false);
|
||||
}
|
||||
|
||||
pthread_mutexattr_destroy(&mutexAttribute);*/
|
||||
}
|
||||
|
||||
ReferenceCountingLock::~ReferenceCountingLock()
|
||||
{
|
||||
// pthread_mutex_destroy(_mutex);
|
||||
}
|
||||
|
||||
bool ReferenceCountingLock::acquire(int64 /*msecs*/)
|
||||
{
|
||||
_mutex.lock();
|
||||
return true;
|
||||
/* struct timespec deltatime;
|
||||
if(msecs > 0)
|
||||
{
|
||||
deltatime.tv_sec = msecs / 1000;
|
||||
deltatime.tv_nsec = (msecs % 1000) * 1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
deltatime.tv_sec = 0;
|
||||
deltatime.tv_nsec = 0;
|
||||
}
|
||||
|
||||
int32 retval = pthread_mutex_timedlock(_mutex, &deltatime);
|
||||
if(retval == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
*/
|
||||
}
|
||||
|
||||
void ReferenceCountingLock::release()
|
||||
{
|
||||
_mutex.unlock();
|
||||
/* int retval = pthread_mutex_unlock(_mutex);
|
||||
if(retval != 0)
|
||||
{
|
||||
//string errMsg = "Error: pthread_mutex_unlock failed: " + string(strerror(retval));
|
||||
//TODO do something?
|
||||
}*/
|
||||
}
|
||||
|
||||
// TODO use atomic primitive impl.
|
||||
int ReferenceCountingLock::increment()
|
||||
{
|
||||
Lock guard(_countMutex);
|
||||
++_references;
|
||||
return _references;
|
||||
}
|
||||
|
||||
int ReferenceCountingLock::decrement()
|
||||
{
|
||||
Lock guard(_countMutex);
|
||||
--_references;
|
||||
return _references;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
87
src/utils/referenceCountingLock.h
Normal file
87
src/utils/referenceCountingLock.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef REFERENCECOUNTINGLOCK_H
|
||||
#define REFERENCECOUNTINGLOCK_H
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define referenceCountingLockEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#ifdef referenceCountingLockEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef referenceCountingLockEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
/**
|
||||
* Reference counting mutex implementation w/ deadlock detection.
|
||||
* Synchronization helper class used (intended for use) for activation/deactivation synchronization.
|
||||
* This class enforces <code>attempt</code> method of acquiring the locks to prevent deadlocks.
|
||||
* Class also offers reference counting.
|
||||
* (NOTE: automatic lock counting was not implemented due to imperfect usage.)
|
||||
*
|
||||
*/
|
||||
class ReferenceCountingLock
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(ReferenceCountingLock);
|
||||
|
||||
/**
|
||||
* Constructor of <code>ReferenceCountingLock</code>.
|
||||
* After construction lock is free and reference count equals <code>1</code>.
|
||||
*/
|
||||
ReferenceCountingLock();
|
||||
/**
|
||||
* Destructor of <code>ReferenceCountingLock</code>.
|
||||
*/
|
||||
virtual ~ReferenceCountingLock();
|
||||
/**
|
||||
* Attempt to acquire lock.
|
||||
*
|
||||
* NOTE: Argument msecs is currently not supported due to
|
||||
* Darwin OS not supporting pthread_mutex_timedlock. May be changed in the future.
|
||||
*
|
||||
* @param msecs the number of milleseconds to wait.
|
||||
* An argument less than or equal to zero means not to wait at all.
|
||||
*
|
||||
* @return <code>true</code> if acquired, <code>false</code> otherwise.
|
||||
* NOTE: currently this routine always returns true. Look above for explanation.
|
||||
*
|
||||
*/
|
||||
bool acquire(epics::pvData::int64 msecs);
|
||||
/**
|
||||
* Release previously acquired lock.
|
||||
*/
|
||||
void release();
|
||||
/**
|
||||
* Increment number of references.
|
||||
*
|
||||
* @return number of references.
|
||||
*/
|
||||
int increment();
|
||||
/**
|
||||
* Decrement number of references.
|
||||
*
|
||||
* @return number of references.
|
||||
*/
|
||||
int decrement();
|
||||
private:
|
||||
int _references;
|
||||
epics::pvData::Mutex _mutex;
|
||||
epics::pvData::Mutex _countMutex;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* REFERENCECOUNTINGLOCK_H */
|
||||
11
src/v3ioc/Makefile
Normal file
11
src/v3ioc/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
# This is a Makefile fragment, see ../Makefile
|
||||
|
||||
SRC_DIRS += $(PVACCESS_SRC)/v3ioc
|
||||
|
||||
INC += syncChannelFind.h
|
||||
|
||||
DBD += PVAServerRegister.dbd
|
||||
DBD += PVAClientRegister.dbd
|
||||
|
||||
LIBSRCS += PVAServerRegister.cpp
|
||||
LIBSRCS += PVAClientRegister.cpp
|
||||
72
src/v3ioc/PVAClientRegister.cpp
Normal file
72
src/v3ioc/PVAClientRegister.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/*PVAClientRegister.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.08.05
|
||||
*/
|
||||
|
||||
/* Author: Marty Kraimer */
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
#include <iocsh.h>
|
||||
|
||||
#include <epicsExport.h>
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/clientFactory.h>
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
|
||||
|
||||
static const iocshFuncDef startPVAClientFuncDef = {
|
||||
"startPVAClient", 0, 0
|
||||
};
|
||||
extern "C" void startPVAClient(const iocshArgBuf *args)
|
||||
{
|
||||
ClientFactory::start();
|
||||
}
|
||||
|
||||
static const iocshFuncDef stopPVAClientFuncDef = {
|
||||
"stopPVAClient", 0, 0
|
||||
};
|
||||
extern "C" void stopPVAClient(const iocshArgBuf *args)
|
||||
{
|
||||
ClientFactory::stop();
|
||||
}
|
||||
|
||||
|
||||
static void startPVAClientRegister(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&startPVAClientFuncDef, startPVAClient);
|
||||
}
|
||||
}
|
||||
|
||||
static void stopPVAClientRegister(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&stopPVAClientFuncDef, stopPVAClient);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
epicsExportRegistrar(startPVAClientRegister);
|
||||
epicsExportRegistrar(stopPVAClientRegister);
|
||||
2
src/v3ioc/PVAClientRegister.dbd
Normal file
2
src/v3ioc/PVAClientRegister.dbd
Normal file
@@ -0,0 +1,2 @@
|
||||
registrar("startPVAClientRegister")
|
||||
registrar("stopPVAClientRegister")
|
||||
129
src/v3ioc/PVAServerRegister.cpp
Normal file
129
src/v3ioc/PVAServerRegister.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
/*PVAServerRegister.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
* @date 2013.07.24
|
||||
*/
|
||||
|
||||
/* Author: Marty Kraimer */
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
#include <cantProceed.h>
|
||||
#include <epicsStdio.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsThread.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
#include <epicsExport.h>
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/serverContext.h>
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
|
||||
class PVAServerCTX;
|
||||
typedef std::tr1::shared_ptr<PVAServerCTX> PVAServerCTXPtr;
|
||||
|
||||
class PVAServerCTX :
|
||||
public std::tr1::enable_shared_from_this<PVAServerCTX>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVAServerCTX);
|
||||
static PVAServerCTXPtr getPVAServerCTX();
|
||||
void start();
|
||||
void stop();
|
||||
virtual ~PVAServerCTX() {}
|
||||
private:
|
||||
PVAServerCTX() {}
|
||||
shared_pointer getPtrSelf()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
ServerContext::shared_pointer ctx;
|
||||
};
|
||||
|
||||
void PVAServerCTX::start()
|
||||
{
|
||||
if(ctx!=NULL) {
|
||||
cout<< "PVAServer already started" << endl;
|
||||
return;
|
||||
}
|
||||
ctx = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
|
||||
}
|
||||
|
||||
void PVAServerCTX::stop()
|
||||
{
|
||||
if(ctx==NULL) {
|
||||
cout<< "PVAServer already stopped" << endl;
|
||||
return;
|
||||
}
|
||||
ctx->destroy();
|
||||
ctx.reset();
|
||||
epicsThreadSleep(1.0);
|
||||
}
|
||||
|
||||
PVAServerCTXPtr PVAServerCTX::getPVAServerCTX()
|
||||
{
|
||||
static PVAServerCTXPtr pvPVAServerCTX;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
|
||||
if(pvPVAServerCTX==NULL) {
|
||||
pvPVAServerCTX = PVAServerCTXPtr(new PVAServerCTX());
|
||||
}
|
||||
return pvPVAServerCTX;
|
||||
}
|
||||
|
||||
|
||||
static const iocshFuncDef startPVAServerFuncDef = {
|
||||
"startPVAServer", 0, 0
|
||||
};
|
||||
extern "C" void startPVAServer(const iocshArgBuf *args)
|
||||
{
|
||||
PVAServerCTX::getPVAServerCTX()->start();
|
||||
}
|
||||
|
||||
static const iocshFuncDef stopPVAServerFuncDef = {
|
||||
"stopPVAServer", 0, 0
|
||||
};
|
||||
extern "C" void stopPVAServer(const iocshArgBuf *args)
|
||||
{
|
||||
PVAServerCTX::getPVAServerCTX()->stop();
|
||||
}
|
||||
|
||||
|
||||
static void startPVAServerRegister(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&startPVAServerFuncDef, startPVAServer);
|
||||
}
|
||||
}
|
||||
|
||||
static void stopPVAServerRegister(void)
|
||||
{
|
||||
static int firstTime = 1;
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
iocshRegister(&stopPVAServerFuncDef, stopPVAServer);
|
||||
}
|
||||
}
|
||||
|
||||
epicsExportRegistrar(startPVAServerRegister);
|
||||
epicsExportRegistrar(stopPVAServerRegister);
|
||||
2
src/v3ioc/PVAServerRegister.dbd
Normal file
2
src/v3ioc/PVAServerRegister.dbd
Normal file
@@ -0,0 +1,2 @@
|
||||
registrar("startPVAServerRegister")
|
||||
registrar("stopPVAServerRegister")
|
||||
67
src/v3ioc/syncChannelFind.h
Normal file
67
src/v3ioc/syncChannelFind.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* syncChannelFind.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvData is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
* @date 2014.02
|
||||
*/
|
||||
/**
|
||||
* This is an implementation of ChannelFind that is appropriate for all channel
|
||||
* providers that can synchronously determine if the provider has the channel.
|
||||
*/
|
||||
#ifndef SYNCCHANNELFIND_H
|
||||
#define SYNCCHANNELFIND_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#ifdef epicsExportSharedSymbols
|
||||
# define syncChannelFindEpicsExportSharedSymbols
|
||||
# undef epicsExportSharedSymbols
|
||||
#endif
|
||||
#include <pv/pvData.h>
|
||||
#ifdef syncChannelFindEpicsExportSharedSymbols
|
||||
# define epicsExportSharedSymbols
|
||||
# undef syncChannelFindEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
|
||||
namespace epics { namespace pvAccess {
|
||||
|
||||
class SyncChannelFind : public ChannelFind
|
||||
{
|
||||
public:
|
||||
typedef std::tr1::shared_ptr<SyncChannelFind> shared_pointer;
|
||||
|
||||
SyncChannelFind(ChannelProvider::shared_pointer &provider) : m_provider(provider)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~SyncChannelFind() {}
|
||||
|
||||
virtual void destroy(){}
|
||||
|
||||
virtual ChannelProvider::shared_pointer getChannelProvider()
|
||||
{
|
||||
return m_provider.lock();
|
||||
};
|
||||
|
||||
virtual void cancelChannelFind() {}
|
||||
|
||||
private:
|
||||
ChannelProvider::weak_pointer m_provider;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}}
|
||||
#endif /* SYNCCHANNELFIND_H */
|
||||
Reference in New Issue
Block a user