Files
pva2pva/pdbApp/pdb.cpp
2016-03-02 17:32:53 -05:00

224 lines
7.1 KiB
C++

#include <vector>
#include <utility>
#include <errlog.h>
#include "helper.h"
#include "pdbsingle.h"
#include "pdbgroup.h"
#include "pvif.h"
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
int PDBProviderDebug = 3;
namespace {
struct GroupMemberInfo {
GroupMemberInfo(const std::string& a, const std::string& b) :pvname(a), pvfldname(b) {}
std::string pvname, pvfldname;
};
struct GroupInfo {
GroupInfo(const std::string& name) : name(name) {}
std::string name;
typedef std::vector<GroupMemberInfo> members_t;
members_t members;
};
}
PDBProvider::PDBProvider()
{
typedef std::map<std::string, GroupInfo> groups_t;
groups_t groups;
for(pdbRecordIterator rec; !rec.done(); rec.next())
{
const char * const group = rec.info("pdbGroup");
if(!group)
continue;
/* syntax:
* info(pdbGroup, "<groupname>|field=FLD|other=FLD2?{}|...")
*/
try {
std::string recbase(rec.name());
recbase+=".";
const char *pos=strchr(group, '|');
if(!pos)
throw std::runtime_error("missing '|' after group name");
else if(pos==group)
throw std::runtime_error("empty group name not allowed");
std::string groupname(group, pos-group);
groups_t::iterator it = groups.find(groupname);
if(it==groups.end()) {
// lazy creation of group
std::pair<groups_t::iterator, bool> ins(groups.insert(std::make_pair(groupname, GroupInfo(groupname))));
it = ins.first;
}
GroupInfo& info = it->second;
pos++;
while(pos && *pos) {
const char *next = strchr(pos, '|'),
*equal= strchr(pos, '=');
if(!equal || (next && equal>next))
throw std::runtime_error("expected '='");
info.members.push_back(GroupMemberInfo(recbase + (next ? std::string(equal+1, next-equal-1) : std::string(equal+1)),
std::string(pos, equal-pos)));
if(PDBProviderDebug>2) {
fprintf(stderr, " pdbGroup '%s' add '%s'='%s'\n",
info.name.c_str(),
info.members.back().pvfldname.c_str(),
info.members.back().pvname.c_str());
}
if(next) next++;
pos = next;
}
} catch(std::exception& e) {
fprintf(stderr, "%s: has invalid pdbGroup spec '%s': %s\n", rec.name(), group, e.what());
}
}
FOREACH(it, end, groups)
{
GroupInfo &info=it->second;
try{
if(persist_pv_map.find(info.name)!=persist_pv_map.end())
throw std::runtime_error("name already in used");
const size_t nchans = info.members.size();
PDBGroupPV::shared_pointer pv(new PDBGroupPV());
pv->name = info.name;
pv->attachments.resize(nchans);
//pv->chan.resize(nchans);
pvd::shared_vector<DBCH> chans(nchans);
std::vector<dbCommon*> records(nchans);
pvd::FieldBuilderPtr builder(pvd::getFieldCreate()->createFieldBuilder());
for(size_t i=0; i<nchans; i++)
{
GroupMemberInfo &mem = info.members[i];
DBCH chan(mem.pvname);
builder->add(mem.pvfldname, PVIF::dtype(chan));
pv->attachments[i] = mem.pvfldname;
records[i] = dbChannelRecord(chan);
chans[i].swap(chan);
}
pv->fielddesc = builder->createStructure();
pv->chan.swap(chans);
DBManyLock L(&records[0], records.size(), 0);
pv->locker.swap(L);
persist_pv_map[info.name] = pv;
}catch(std::exception& e){
fprintf(stderr, "%s: pdbGroup not created: %s\n", info.name.c_str(), e.what());
}
}
}
PDBProvider::~PDBProvider() {}
void PDBProvider::destroy() {}
std::string PDBProvider::getProviderName() { return "PDBProvider"; }
namespace {
struct ChannelFindRequesterNOOP : public pva::ChannelFind
{
const pva::ChannelProvider::weak_pointer provider;
ChannelFindRequesterNOOP(const pva::ChannelProvider::shared_pointer& prov) : provider(prov) {}
virtual ~ChannelFindRequesterNOOP() {}
virtual void destroy() {}
virtual std::tr1::shared_ptr<pva::ChannelProvider> getChannelProvider() { return provider.lock(); }
virtual void cancel() {}
};
}
pva::ChannelFind::shared_pointer
PDBProvider::channelFind(const std::string &channelName, const pva::ChannelFindRequester::shared_pointer &requester)
{
pva::ChannelFind::shared_pointer ret(new ChannelFindRequesterNOOP(shared_from_this()));
bool found = false;
{
epicsGuard<epicsMutex> G(transient_pv_map.mutex());
if(persist_pv_map.find(channelName)!=persist_pv_map.end()
|| transient_pv_map.find(channelName)
|| dbChannelTest(channelName.c_str())==0)
found = true;
}
requester->channelFindResult(pvd::Status(), ret, found);
return ret;
}
pva::ChannelFind::shared_pointer
PDBProvider::channelList(pva::ChannelListRequester::shared_pointer const & requester)
{
pva::ChannelFind::shared_pointer ret;
requester->channelListResult(pvd::Status(pvd::Status::STATUSTYPE_ERROR, "not supported"),
ret, pvd::PVStringArray::const_svector(), true);
return ret;
}
pva::Channel::shared_pointer
PDBProvider::createChannel(std::string const & channelName,
pva::ChannelRequester::shared_pointer const & channelRequester,
short priority)
{
return createChannel(channelName, channelRequester, priority, "???");
}
pva::Channel::shared_pointer
PDBProvider::createChannel(std::string const & channelName,
pva::ChannelRequester::shared_pointer const & requester,
short priority, std::string const & address)
{
pva::Channel::shared_pointer ret;
PDBPV::shared_pointer pv;
pvd::Status status;
{
epicsGuard<epicsMutex> G(transient_pv_map.mutex());
pv = transient_pv_map.find(channelName);
if(!pv) {
persist_pv_map_t::const_iterator it=persist_pv_map.find(channelName);
if(it!=persist_pv_map.end()) {
pv = it->second;
}
}
if(!pv) {
dbChannel *pchan = dbChannelCreate(channelName.c_str());
if(pchan) {
DBCH chan(pchan);
pv.reset(new PDBSinglePV(chan, shared_from_this()));
transient_pv_map.insert(channelName, pv);
}
}
}
if(pv) {
ret = pv->connect(shared_from_this(), requester);
}
if(!ret) {
status = pvd::Status(pvd::Status::STATUSTYPE_ERROR, "not found");
}
requester->channelCreated(status, ret);
return ret;
}