Add SharedPV
This commit is contained in:
@ -16,6 +16,15 @@ monitorme_SRCS = monitorme.cpp
|
||||
TESTPROD_HOST += spamme
|
||||
spamme_SRCS = spamme.cpp
|
||||
|
||||
TESTPROD_HOST += mailbox
|
||||
mailbox_SRCS += mailbox.cpp
|
||||
|
||||
TESTPROD_HOST += epicschat
|
||||
epicschat_SRCS += epicschat.cpp
|
||||
|
||||
TESTPROD_HOST += lazycounter
|
||||
lazycounter_SRCS += lazycounter.cpp
|
||||
|
||||
TESTPROD_HOST += miniget
|
||||
miniget_SRCS = miniget.cpp
|
||||
|
||||
|
200
examples/epicschat.cpp
Normal file
200
examples/epicschat.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
/**
|
||||
* 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 <stdio.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <signal.h>
|
||||
#define USE_SIGNAL
|
||||
#endif
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
#include <epicsEvent.h>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pva/server.h>
|
||||
#include <pva/sharedstate.h>
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
namespace pva = epics::pvAccess;
|
||||
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
typedef epicsGuardRelease<epicsMutex> UnGuard;
|
||||
|
||||
namespace {
|
||||
|
||||
epicsEvent done;
|
||||
|
||||
#ifdef USE_SIGNAL
|
||||
void alldone(int num)
|
||||
{
|
||||
(void)num;
|
||||
done.signal();
|
||||
}
|
||||
#endif
|
||||
|
||||
static pvd::StructureConstPtr string_type(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->add("value", pvd::pvString)
|
||||
->createStructure());
|
||||
|
||||
struct ChatHandler : public pvas::SharedPV::Handler
|
||||
{
|
||||
POINTER_DEFINITIONS(ChatHandler);
|
||||
virtual ~ChatHandler() {
|
||||
printf("Cleanup Room\n");
|
||||
}
|
||||
virtual void onLastDisconnect(pvas::SharedPV& self) {
|
||||
printf("Close Room %p\n", &self);
|
||||
}
|
||||
virtual void onPut(pvas::SharedPV& self, pvas::Operation& op) {
|
||||
pva::ChannelRequester::shared_pointer req(op.getChannel()->getChannelRequester());
|
||||
std::ostringstream strm;
|
||||
|
||||
if(req) {
|
||||
strm<<req->getRequesterName()<<" says ";
|
||||
} else {
|
||||
op.complete(pvd::Status::error("Defuct Put"));
|
||||
return;
|
||||
}
|
||||
|
||||
strm<<op.value().getSubFieldT<pvd::PVString>("value")->get();
|
||||
|
||||
pvd::PVStructurePtr replacement(pvd::getPVDataCreate()->createPVStructure(string_type));
|
||||
|
||||
replacement->getSubFieldT<pvd::PVString>("value")->put(strm.str());
|
||||
|
||||
self.post(*replacement, op.changed());
|
||||
op.complete();
|
||||
}
|
||||
};
|
||||
|
||||
struct RoomHandler : public pvas::DynamicProvider::Handler,
|
||||
public std::tr1::enable_shared_from_this<RoomHandler>
|
||||
{
|
||||
POINTER_DEFINITIONS(RoomHandler);
|
||||
|
||||
const std::string prefix;
|
||||
|
||||
mutable epicsMutex mutex;
|
||||
|
||||
typedef std::map<std::string, pvas::SharedPV::weak_pointer> rooms_t;
|
||||
rooms_t rooms;
|
||||
|
||||
RoomHandler(const std::string& prefix) :prefix(prefix) {}
|
||||
virtual ~RoomHandler() {}
|
||||
|
||||
virtual void hasChannels(pvas::DynamicProvider::search_type& names) OVERRIDE FINAL {
|
||||
for(pvas::DynamicProvider::search_type::iterator it(names.begin()), end(names.end());
|
||||
it != end; ++it)
|
||||
{
|
||||
if(it->name().find(prefix)==0)
|
||||
it->claim();
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::tr1::shared_ptr<epics::pvAccess::Channel> createChannel(const std::tr1::shared_ptr<epics::pvAccess::ChannelProvider>& provider,
|
||||
const std::string& name,
|
||||
const std::tr1::shared_ptr<epics::pvAccess::ChannelRequester>& requester) OVERRIDE FINAL
|
||||
{
|
||||
pva::Channel::shared_pointer ret;
|
||||
|
||||
pvas::SharedPV::shared_pointer pv;
|
||||
bool created = false;
|
||||
if(name.find(prefix)==0)
|
||||
{
|
||||
Guard G(mutex);
|
||||
|
||||
rooms_t::iterator it(rooms.find(name));
|
||||
if(it!=rooms.end()) {
|
||||
// re-use existing?
|
||||
pv = it->second.lock();
|
||||
}
|
||||
|
||||
// rather than deal with wrapped shared_ptr to remove PVs
|
||||
// as they are destroyed, just sweep each time a new channel is created
|
||||
for(rooms_t::iterator next(rooms.begin()), end(rooms.end()); next!=end;) {
|
||||
rooms_t::iterator cur(next++);
|
||||
if(cur->second.expired())
|
||||
rooms.erase(cur);
|
||||
}
|
||||
|
||||
if(!pv) {
|
||||
// nope
|
||||
ChatHandler::shared_pointer handler(new ChatHandler);
|
||||
pv = pvas::SharedPV::build(handler);
|
||||
|
||||
rooms[name] = pv;
|
||||
created = true;
|
||||
}
|
||||
|
||||
}
|
||||
// unlock
|
||||
|
||||
if(pv) {
|
||||
if(created) {
|
||||
pv->open(string_type);
|
||||
|
||||
// set a non-default initial value so that if we are connecting for
|
||||
// a get, then there will be something to be got.
|
||||
pvd::PVStructurePtr initial(pvd::getPVDataCreate()->createPVStructure(string_type));
|
||||
pvd::PVStringPtr value(initial->getSubFieldT<pvd::PVString>("value"));
|
||||
value->put("Created!");
|
||||
|
||||
pv->post(*initial, pvd::BitSet().set(value->getFieldOffset()));
|
||||
printf("New Room: '%s' for %s as %p\n", name.c_str(), requester->getRequesterName().c_str(), pv.get());
|
||||
} else {
|
||||
printf("Attach Room: '%s' for %s as %p\n", name.c_str(), requester->getRequesterName().c_str(), pv.get());
|
||||
}
|
||||
|
||||
ret = pv->connect(provider, name, requester);
|
||||
} else {
|
||||
// mis-matched prefix
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
}//namespace
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
try {
|
||||
if(argc<=1) {
|
||||
fprintf(stderr, "Usage: %s <prefix>", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
RoomHandler::shared_pointer handler(new RoomHandler(argv[1]));
|
||||
|
||||
pvas::DynamicProvider provider("chat", handler);
|
||||
|
||||
pva::ServerContext::shared_pointer server(pva::ServerContext::create(
|
||||
pva::ServerContext::Config()
|
||||
// use default config from environment
|
||||
.provider(provider.provider())
|
||||
));
|
||||
|
||||
#ifdef USE_SIGNAL
|
||||
signal(SIGINT, alldone);
|
||||
signal(SIGTERM, alldone);
|
||||
signal(SIGQUIT, alldone);
|
||||
#endif
|
||||
server->printInfo();
|
||||
|
||||
done.wait();
|
||||
|
||||
} catch(std::exception& e){
|
||||
std::cerr<<"Error: "<<e.what()<<"\n";
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
254
examples/lazycounter.cpp
Normal file
254
examples/lazycounter.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/* A counter which only ticks if at least one client is connected to it.
|
||||
*
|
||||
* Also, the type changes (toggles between int and real) each time that
|
||||
* the first client connects.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <signal.h>
|
||||
#define USE_SIGNAL
|
||||
#endif
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
#include <epicsThread.h>
|
||||
#include <epicsEvent.h>
|
||||
|
||||
#include <pv/timer.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pva/server.h>
|
||||
#include <pva/sharedstate.h>
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
namespace pva = epics::pvAccess;
|
||||
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
typedef epicsGuardRelease<epicsMutex> UnGuard;
|
||||
|
||||
namespace {
|
||||
|
||||
epicsEvent done;
|
||||
|
||||
#ifdef USE_SIGNAL
|
||||
void alldone(int num)
|
||||
{
|
||||
(void)num;
|
||||
done.signal();
|
||||
}
|
||||
#endif
|
||||
|
||||
// for demonstration purposes, we will switch between two different types.
|
||||
|
||||
static pvd::StructureConstPtr int_type(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->add("value", pvd::pvULong)
|
||||
->createStructure());
|
||||
|
||||
static pvd::StructureConstPtr flt_type(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->add("value", pvd::pvDouble)
|
||||
->createStructure());
|
||||
|
||||
struct Counter : public pvas::SharedPV::Handler,
|
||||
public pvd::TimerCallback,
|
||||
public pva::Destroyable
|
||||
{
|
||||
POINTER_DEFINITIONS(Counter);
|
||||
|
||||
// our name, internally only for logging.
|
||||
// The searchable channel name is given to the StaticProvider
|
||||
const std::string name;
|
||||
const pvas::SharedPV::weak_pointer pv;
|
||||
pvd::Timer& timer_queue;
|
||||
|
||||
// const after build()
|
||||
weak_pointer internal_self;
|
||||
|
||||
mutable epicsMutex mutex;
|
||||
|
||||
bool queued; // are we in the Timer queue?
|
||||
|
||||
pvd::uint64 count;
|
||||
bool typesel;
|
||||
pvd::PVStructurePtr scratch;
|
||||
pvd::PVScalarPtr scratch_value;
|
||||
|
||||
static Counter::shared_pointer build(const pvas::SharedPV::shared_pointer& pv,
|
||||
const std::string& name,
|
||||
pvd::Timer& timer_queue) {
|
||||
Counter::shared_pointer internal(new Counter(pv, name, timer_queue)),
|
||||
external(internal.get(), pva::Destroyable::cleaner(internal));
|
||||
// we give out internal ref (to Timer)
|
||||
internal->internal_self = internal;
|
||||
// SharedPV keeps us alive.
|
||||
// destroy() is called when SharedPV is destroyed (or Handler is replace)
|
||||
pv->setHandler(external);
|
||||
return external;
|
||||
}
|
||||
|
||||
Counter(const pvas::SharedPV::shared_pointer& pv, const std::string& name, pvd::Timer& timer_queue)
|
||||
:name(name)
|
||||
,pv(pv)
|
||||
,timer_queue(timer_queue)
|
||||
,queued(false)
|
||||
,count(0u)
|
||||
,typesel(false)
|
||||
{}
|
||||
virtual ~Counter() {
|
||||
printf("%s: destroy\n", name.c_str());
|
||||
}
|
||||
|
||||
virtual void destroy() OVERRIDE FINAL {
|
||||
|
||||
{
|
||||
Guard G(mutex);
|
||||
if(!queued) return;
|
||||
queued = false;
|
||||
}
|
||||
printf("%s: shutdown\n", name.c_str());
|
||||
timer_queue.cancel(shared_pointer(internal_self));
|
||||
}
|
||||
|
||||
// when we go from zero clients connected to more than one client connected.
|
||||
virtual void onFirstConnect(const pvas::SharedPV::shared_pointer& pv) OVERRIDE FINAL {
|
||||
{
|
||||
Guard G(mutex);
|
||||
assert(!queued);
|
||||
queued = true;
|
||||
}
|
||||
printf("%s: starting\n", name.c_str());
|
||||
// timer first expires after 1 second, then again every second.
|
||||
// so any operation (including pvinfo) will take 1 second.
|
||||
timer_queue.schedulePeriodic(shared_pointer(internal_self), 1.0, 1.0);
|
||||
}
|
||||
|
||||
// timer expires
|
||||
virtual void callback() OVERRIDE FINAL {
|
||||
bool open;
|
||||
pvd::uint64 next;
|
||||
pvd::PVStructurePtr top;
|
||||
pvd::BitSet vmask;
|
||||
{
|
||||
Guard G(mutex);
|
||||
if(!queued) return;
|
||||
|
||||
open = !scratch;
|
||||
if(open) {
|
||||
// first expiration after onFirstConnect()
|
||||
// select type.
|
||||
pvd::StructureConstPtr type = typesel ? int_type : flt_type;
|
||||
typesel = !typesel;
|
||||
|
||||
scratch = pvd::getPVDataCreate()->createPVStructure(type);
|
||||
scratch_value = scratch->getSubFieldT<pvd::PVScalar>("value");
|
||||
}
|
||||
|
||||
// store counter value
|
||||
next = count++;
|
||||
scratch_value->putFrom(next);
|
||||
vmask.set(scratch_value->getFieldOffset());
|
||||
|
||||
// We will use the PVStructure when the lock is not held.
|
||||
// This is safe as it is only modified from this (Timer)
|
||||
// thread.
|
||||
top = scratch;
|
||||
}
|
||||
|
||||
pvas::SharedPV::shared_pointer pv(this->pv);
|
||||
|
||||
if(open) {
|
||||
// go from closed -> open.
|
||||
// provide initial value (and new type)
|
||||
printf("%s: open %llu\n", name.c_str(), (unsigned long long)next);
|
||||
pv->open(*top, vmask);
|
||||
} else {
|
||||
// post update
|
||||
printf("%s: tick %llu\n", name.c_str(), (unsigned long long)next);
|
||||
pv->post(*top, vmask);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void timerStopped() OVERRIDE FINAL {}
|
||||
|
||||
// when we go from 1 client connected to zero clients connected.
|
||||
virtual void onLastDisconnect(const pvas::SharedPV::shared_pointer& pv) OVERRIDE FINAL {
|
||||
bool close;
|
||||
bool cancel;
|
||||
{
|
||||
Guard G(mutex);
|
||||
cancel = queued;
|
||||
queued = false;
|
||||
close = !!scratch;
|
||||
|
||||
scratch.reset();
|
||||
scratch_value.reset();
|
||||
}
|
||||
// !close implies only all clients disconnect before timer expires the first time
|
||||
if(close) {
|
||||
printf("%s: close\n", name.c_str());
|
||||
pv->close();
|
||||
}
|
||||
if(cancel) {
|
||||
timer_queue.cancel(shared_pointer(internal_self));
|
||||
}
|
||||
printf("%s: stopping\n", name.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
try {
|
||||
if(argc<=1) {
|
||||
fprintf(stderr, "Usage: %s <pvname> ...\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
pvd::Timer timer_queue("counters", (pvd::ThreadPriority)epicsThreadPriorityMedium);
|
||||
|
||||
pvas::StaticProvider provider("counters"); // provider name "counters" is arbitrary
|
||||
|
||||
for(int i=1; i<argc; i++) {
|
||||
pvas::SharedPV::shared_pointer pv(pvas::SharedPV::buildReadOnly());
|
||||
Counter::shared_pointer cnt(Counter::build(pv, argv[i], timer_queue));
|
||||
provider.add(argv[i], pv);
|
||||
printf("Add counter '%s'\n", argv[i]);
|
||||
}
|
||||
|
||||
// create and run network server
|
||||
pva::ServerContext::shared_pointer server(pva::ServerContext::create(
|
||||
pva::ServerContext::Config()
|
||||
// use default config from environment
|
||||
.provider(provider.provider())
|
||||
));
|
||||
|
||||
#ifdef USE_SIGNAL
|
||||
signal(SIGINT, alldone);
|
||||
signal(SIGTERM, alldone);
|
||||
signal(SIGQUIT, alldone);
|
||||
#endif
|
||||
server->printInfo();
|
||||
|
||||
printf("Running with counters\n");
|
||||
|
||||
done.wait();
|
||||
|
||||
timer_queue.close(); // joins timer worker
|
||||
|
||||
} catch(std::exception& e){
|
||||
std::cerr<<"Error: "<<e.what()<<"\n";
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
116
examples/mailbox.cpp
Normal file
116
examples/mailbox.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 <stdio.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <signal.h>
|
||||
#define USE_SIGNAL
|
||||
#endif
|
||||
|
||||
#include <epicsEvent.h>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/serverContext.h>
|
||||
#include <pva/server.h>
|
||||
#include <pva/sharedstate.h>
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
namespace pva = epics::pvAccess;
|
||||
|
||||
namespace {
|
||||
|
||||
epicsEvent done;
|
||||
|
||||
#ifdef USE_SIGNAL
|
||||
void alldone(int num)
|
||||
{
|
||||
(void)num;
|
||||
done.signal();
|
||||
}
|
||||
#endif
|
||||
|
||||
static pvd::StructureConstPtr string_type(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->add("value", pvd::pvString)
|
||||
->createStructure());
|
||||
|
||||
static pvd::StructureConstPtr int_type(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->add("value", pvd::pvInt)
|
||||
->createStructure());
|
||||
|
||||
static pvd::StructureConstPtr real_type(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->add("value", pvd::pvDouble)
|
||||
->createStructure());
|
||||
|
||||
}//namespace
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
try {
|
||||
if(argc<=1) {
|
||||
fprintf(stderr, "Usage: %s <pvname[=type]> ...\n type: string, int, real", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// container for PVs
|
||||
pvas::StaticProvider provider("mailbox"); // provider name "mailbox" is arbitrary
|
||||
|
||||
for(int i=1; i<argc; i++) {
|
||||
|
||||
std::string name(argv[i]), type("string");
|
||||
|
||||
size_t sep = name.find('=');
|
||||
if(sep != name.npos) {
|
||||
if(sep==0 || sep==name.size()) {
|
||||
fprintf(stderr, "Invalid: '%s'\n", argv[i]);
|
||||
return 1;
|
||||
}
|
||||
type = name.substr(sep+1);
|
||||
name = name.substr(0, sep-1);
|
||||
}
|
||||
|
||||
pvas::SharedPV::shared_pointer pv(pvas::SharedPV::buildMailbox());
|
||||
|
||||
// open() the PV, associates type
|
||||
if(type=="string") {
|
||||
pv->open(string_type);
|
||||
} else if(type=="int") {
|
||||
pv->open(int_type);
|
||||
} else if(type=="real") {
|
||||
pv->open(real_type);
|
||||
} else {
|
||||
fprintf(stderr, "Unknown type '%s'\n", type.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// add to container
|
||||
provider.add(argv[1], pv);
|
||||
}
|
||||
|
||||
// create and run network server
|
||||
pva::ServerContext::shared_pointer server(pva::ServerContext::create(
|
||||
pva::ServerContext::Config()
|
||||
// use default config from environment
|
||||
.provider(provider.provider())
|
||||
));
|
||||
|
||||
#ifdef USE_SIGNAL
|
||||
signal(SIGINT, alldone);
|
||||
signal(SIGTERM, alldone);
|
||||
signal(SIGQUIT, alldone);
|
||||
#endif
|
||||
server->printInfo();
|
||||
|
||||
printf("Running with mailbox '%s'\n", argv[1]);
|
||||
|
||||
done.wait();
|
||||
|
||||
} catch(std::exception& e){
|
||||
std::cerr<<"Error: "<<e.what()<<"\n";
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user