13 #include <dbChannel.h> 39 const char sep, *cur, *end;
40 Splitter(
const char *s,
char sep)
44 end = strchr(cur, sep);
47 bool snip(std::string& ret) {
48 if(!cur)
return false;
49 if(end) ret = std::string(cur, end-cur);
50 else ret = std::string(cur);
53 end = strchr(cur, sep);
61 struct GroupMemberInfo {
63 GroupMemberInfo(
const std::string& a,
const std::string& b,
const std::tr1::shared_ptr<PVIFBuilder>& builder)
64 :pvname(a), pvfldname(b), builder(builder), putorder(0) {}
69 typedef std::set<std::string> triggers_t;
71 std::tr1::shared_ptr<PVIFBuilder> builder;
74 bool operator<(
const GroupMemberInfo& o)
const {
75 return putorder<o.putorder;
80 GroupInfo(
const std::string& name) : name(name),atomic(Unset),hastriggers(
false) {}
81 std::string name, structID;
83 typedef std::vector<GroupMemberInfo> members_t;
86 typedef std::map<std::string, size_t> members_map_t;
87 members_map_t members_map;
89 typedef std::set<std::string> triggers_set_t;
90 typedef std::map<std::string, triggers_set_t> triggers_t;
93 enum tribool {Unset,True,False} atomic;
100 typedef std::map<std::string, GroupInfo> groups_t;
107 void resolveTriggers()
109 FOREACH(groups_t::iterator, it, end, groups) {
110 GroupInfo& info = it->second;
112 if(info.hastriggers) {
113 FOREACH(GroupInfo::triggers_t::iterator, it2, end2, info.triggers) {
114 const std::string& src = it2->first;
115 GroupInfo::triggers_set_t& targets = it2->second;
117 GroupInfo::members_map_t::iterator it2x = info.members_map.find(src);
118 if(it2x==info.members_map.end()) {
119 fprintf(
stderr,
"Error: Group \"%s\" defines triggers from non-existant field \"%s\"\n",
120 info.name.c_str(), src.c_str());
123 GroupMemberInfo& srcmem = info.members[it2x->second];
126 fprintf(
stderr,
" pdb trg '%s.%s' -> ",
127 info.name.c_str(), src.c_str());
129 FOREACH(GroupInfo::triggers_set_t::const_iterator, it3, end3, targets) {
130 const std::string& target = *it3;
133 for(
size_t i=0;
i<info.members.size();
i++) {
134 if(info.members[
i].pvname.empty())
136 srcmem.triggers.insert(info.members[
i].pvfldname);
138 fprintf(
stderr,
"%s, ", info.members[
i].pvfldname.c_str());
143 GroupInfo::members_map_t::iterator it3x = info.members_map.find(target);
144 if(it3x==info.members_map.end()) {
145 fprintf(
stderr,
"Error: Group \"%s\" defines triggers to non-existant field \"%s\"\n",
146 info.name.c_str(), target.c_str());
149 const GroupMemberInfo& targetmem = info.members[it3x->second];
151 if(targetmem.pvname.empty()) {
153 fprintf(
stderr,
"<ignore: %s>, ", targetmem.pvfldname.c_str());
157 srcmem.triggers.insert(targetmem.pvfldname);
159 fprintf(
stderr,
"%s, ", targetmem.pvfldname.c_str());
169 FOREACH(GroupInfo::members_t::iterator, it2, end2, info.members) {
170 GroupMemberInfo& mem = *it2;
171 if(mem.pvname.empty())
174 mem.triggers.insert(mem.pvfldname);
180 PDBProcessor() : curgroup(
NULL)
184 const char *json = rec.info(
"Q:group");
186 #ifndef USE_MULTILOCK 190 fprintf(
stderr,
"%s: ignoring info(Q:Group, ...\n", rec.name());
194 fprintf(
stderr,
"%s: info(Q:Group, ...\n", rec.name());
201 fprintf(
stderr,
"%s: warning(s) from info(Q:group, ...\n%s", rec.name(), conf.
warning.c_str());
203 recbase = rec.name();
206 for(GroupConfig::groups_t::const_iterator git=conf.
groups.begin(), gend=conf.
groups.end();
209 const std::string& grpname = git->first;
212 if(dbChannelTest(grpname.c_str())==0) {
213 fprintf(
stderr,
"%s : Error: Group name conflicts with record name. Ignoring...\n", grpname.c_str());
217 groups_t::iterator it = groups.find(grpname);
218 if(it==groups.end()) {
220 std::pair<groups_t::iterator, bool> ins(groups.insert(std::make_pair(grpname, GroupInfo(grpname))));
223 curgroup = &it->second;
225 curgroup->structID = grp.
id;
227 for(GroupConfig::Group::fields_t::const_iterator fit=grp.
fields.begin(), fend=grp.
fields.end();
230 const std::string& fldname = fit->first;
233 GroupInfo::members_map_t::const_iterator oldgrp(curgroup->members_map.find(fldname));
234 if(oldgrp!=curgroup->members_map.end()) {
235 fprintf(
stderr,
"%s.%s Warning: ignoring duplicate mapping %s%s\n",
236 grpname.c_str(), fldname.c_str(),
237 recbase.c_str(), fld.
channel.c_str());
243 curgroup->members.push_back(GroupMemberInfo(fld.
channel.empty() ? fld.
channel : recbase + fld.
channel, fldname, builder));
244 curgroup->members.back().structID = fld.
id;
245 curgroup->members.back().putorder = fld.
putorder;
246 curgroup->members_map[fldname] = (size_t)-1;
249 fprintf(
stderr,
" pdb map '%s.%s' <-> '%s'\n",
250 curgroup->name.c_str(),
251 curgroup->members.back().pvfldname.c_str(),
252 curgroup->members.back().pvname.c_str());
256 GroupInfo::triggers_t::iterator it = curgroup->triggers.find(fldname);
257 if(it==curgroup->triggers.end()) {
258 std::pair<GroupInfo::triggers_t::iterator, bool> ins(curgroup->triggers.insert(
259 std::make_pair(fldname, GroupInfo::triggers_set_t())));
263 Splitter sep(fld.
trigger.c_str(),
',');
266 while(sep.snip(target)) {
267 curgroup->hastriggers =
true;
268 it->second.insert(target);
274 GroupInfo::tribool V = grp.
atomic ? GroupInfo::True : GroupInfo::False;
276 if(curgroup->atomic!=GroupInfo::Unset && curgroup->atomic!=V)
277 fprintf(
stderr,
"%s Warning: pdb atomic setting inconsistent '%s'\n",
278 grpname.c_str(), curgroup->name.c_str());
283 fprintf(
stderr,
" pdb atomic '%s' %s\n",
284 curgroup->name.c_str(), curgroup->atomic ?
"YES" :
"NO");
288 }
catch(std::exception& e){
289 fprintf(
stderr,
"%s: Error parsing info(\"Q:group\", ... : %s\n",
290 rec.record()->name, e.what());
292 #endif // USE_MULTILOCK 297 for(groups_t::iterator it = groups.begin(), end = groups.end(); it!=end; ++it)
299 GroupInfo& info = it->second;
300 std::sort(info.members.begin(),
303 info.members_map.clear();
305 for(
size_t i=0, N=info.members.size();
i<N;
i++)
307 info.members_map[info.members[
i].pvfldname] =
i;
334 ->addNestedStructure(
"_options")
338 ->createStructure());
342 FOREACH(PDBProcessor::groups_t::const_iterator, it, end, proc.groups)
344 const GroupInfo &info=it->second;
346 if(persist_pv_map.find(info.name)!=persist_pv_map.end())
347 throw std::runtime_error(
"name already in used");
351 pv->name = info.name;
353 pv->pgatomic = info.atomic!=GroupInfo::False;
354 pv->monatomic = info.hastriggers;
358 typedef std::map<std::string, size_t> members_map_t;
359 members_map_t members_map;
362 for(
size_t i=0, N=info.members.size();
i<N;
i++)
363 if(!info.members[
i].pvname.empty())
369 std::vector<dbCommon*> records(members.
size());
372 builder = builder->add(
"record", _options);
374 if(!info.structID.empty())
375 builder = builder->setId(info.structID);
377 for(
size_t i=0, J=0, N=info.members.size();
i<N;
i++)
379 const GroupMemberInfo &mem = info.members[
i];
385 for(
size_t j=0; j<parts.
size()-1; j++) {
386 if(parts[j].isArray())
387 builder = builder->addNestedStructureArray(parts[j].name);
389 builder = builder->addNestedStructure(parts[j].name);
393 if(!mem.structID.empty())
394 builder = builder->setId(mem.structID);
397 if(!mem.pvname.empty()) {
398 DBCH temp(mem.pvname);
399 unsigned ftype = dbChannelFieldType(temp);
403 throw std::runtime_error(
"Can't include link fields in group");
409 builder = mem.builder->dtype(builder, parts.
back().
name, chan);
411 builder = mem.builder->dtype(builder,
"", chan);
414 for(
size_t j=0; j<parts.
size()-1; j++)
415 builder = builder->endNested();
418 if(!mem.pvname.empty()) {
419 members_map[mem.pvfldname] = J;
432 records[J] = dbChannelRecord(info.
chan);
437 pv->members.swap(members);
439 pv->fielddesc = builder->createStructure();
440 pv->complete = pvbuilder->createPVStructure(pv->fielddesc);
442 pv->complete->getSubFieldT<
pvd::PVBoolean>(
"record._options.atomic")->
put(pv->monatomic);
444 DBManyLock L(&records[0], records.size(), 0);
448 for(
size_t i=0, J=0, N=info.members.size();
i<N;
i++)
450 const GroupMemberInfo &mem = info.members[
i];
451 if(mem.pvname.empty())
continue;
454 if(mem.triggers.empty())
continue;
456 std::vector<dbCommon*> trig_records;
457 trig_records.reserve(mem.triggers.size());
459 FOREACH(GroupMemberInfo::triggers_t::const_iterator, it, end, mem.triggers) {
460 members_map_t::const_iterator imap(members_map.find(*it));
461 if(imap==members_map.end())
462 throw std::logic_error(
"trigger resolution missed map to non-dbChannel");
464 info.
triggers.push_back(imap->second);
465 trig_records.push_back(records[imap->second]);
468 DBManyLock L(&trig_records[0], trig_records.size(), 0);
472 persist_pv_map[info.name] = pv;
474 }
catch(std::exception& e){
475 fprintf(
stderr,
"%s: Error Group not created: %s\n", info.name.c_str(), e.what());
479 if(!proc.groups.empty()) {
480 fprintf(
stderr,
"Group(s) were defined, but need Base >=3.16.0.2 to function. Ignoring.\n");
482 #endif // USE_MULTILOCK 484 event_context = db_init_events();
486 throw std::runtime_error(
"Failed to create dbEvent context");
489 throw std::runtime_error(
"Failed to stsart dbEvent context");
493 for(persist_pv_map_t::iterator next = persist_pv_map.begin(),
494 end = persist_pv_map.end(),
495 it = next!=end ? next++ : end;
496 it != end; it = next==end ? end : next++)
498 const PDBPV::shared_pointer& ppv = it->second;
523 }
catch(std::exception& e){
524 fprintf(
stderr,
"%s: Error during dbEvent setup : %s\n", pv->
name.c_str(), e.what());
525 persist_pv_map.erase(it);
528 #endif // USE_MULTILOCK 529 epics::atomic::increment(num_instances);
534 epics::atomic::decrement(num_instances);
541 dbEventCtx ctxt =
NULL;
546 persist_pv_map.swap(ppv);
550 if(ctxt) db_close_events(ctxt);
558 const pva::ChannelProvider::weak_pointer provider;
559 ChannelFindRequesterNOOP(
const pva::ChannelProvider::shared_pointer& prov) : provider(prov) {}
560 virtual ~ChannelFindRequesterNOOP() {}
561 virtual void destroy() {}
562 virtual std::tr1::shared_ptr<pva::ChannelProvider> getChannelProvider() {
return provider.lock(); }
563 virtual void cancel() {}
567 pva::ChannelFind::shared_pointer
570 pva::ChannelFind::shared_pointer ret(
new ChannelFindRequesterNOOP(shared_from_this()));
575 if(persist_pv_map.find(channelName)!=persist_pv_map.end()
576 || transient_pv_map.find(channelName)
577 || dbChannelTest(channelName.c_str())==0)
580 requester->channelFindResult(
pvd::Status(), ret, found);
584 pva::ChannelFind::shared_pointer
587 pva::ChannelFind::shared_pointer ret;
591 names.push_back(rec.name());
596 for(persist_pv_map_t::const_iterator it=persist_pv_map.begin(), end=persist_pv_map.end();
599 names.push_back(it->first);
605 pvd::freeze(names),
false);
609 pva::Channel::shared_pointer
611 pva::ChannelRequester::shared_pointer
const & channelRequester,
614 return createChannel(channelName, channelRequester, priority,
"???");
617 pva::Channel::shared_pointer
619 pva::ChannelRequester::shared_pointer
const &
requester,
620 short priority, std::string
const & address)
622 pva::Channel::shared_pointer ret;
623 PDBPV::shared_pointer
pv;
629 pv = transient_pv_map.find(channelName);
631 persist_pv_map_t::const_iterator it=persist_pv_map.find(channelName);
632 if(it!=persist_pv_map.end()) {
637 dbChannel *pchan = dbChannelCreate(channelName.c_str());
640 pv.reset(
new PDBSinglePV(chan, shared_from_this()));
641 transient_pv_map.insert(channelName, pv);
649 ret = pv->connect(shared_from_this(),
requester);
662 Splitter S(pv.c_str(),
'.');
664 while(S.snip(part)) {
666 throw std::runtime_error(
"Empty field component in: "+pv);
668 if(part[part.size()-1]==
']') {
669 const size_t open = part.find_last_of(
'['),
671 bool ok = open!=part.npos;
673 for(
size_t i=open+1; ok &&
i<(N-1);
i++) {
674 ok &= part[
i]>=
'0' && part[
i]<=
'9';
675 index = 10*index + part[
i] -
'0';
678 throw std::runtime_error(
"Invalid field array sub-script in : "+pv);
680 parts.push_back(
Component(part.substr(0, open), index));
687 throw std::runtime_error(
"Empty field name");
688 if(parts.back().isArray())
689 throw std::runtime_error(
"leaf field may not have sub-script : "+pv);
699 for(
size_t i=0, N=parts.size();
i<N;
i++) {
702 throw std::runtime_error(
"mid-field is not structure");
706 if(parts[
i].isArray()) {
709 throw std::runtime_error(
"indexed field is not structure array");
716 if(V.size()<=parts[
i].index || !V[parts[
i].index]) {
721 if(E.size()<=parts[
i].index)
722 E.resize(parts[
i].index+1);
724 if(!E[parts[
i].index])
727 ret = E[parts[
i].index];
732 ret = V[parts[
i].index];
747 for(
size_t i=0, N=parts.size();
i<N;
i++)
754 if(parts[
i].isArray())
755 printf(
"%s[%u]", parts[
i].name.c_str(), (unsigned)parts[
i].index);
757 printf(
"%s", parts[
i].name.c_str());
static PVIFBuilder * create(const std::string &name)
virtual epics::pvAccess::ChannelFind::shared_pointer channelList(epics::pvAccess::ChannelListRequester::shared_pointer const &channelListRequester) OVERRIDE FINAL
#define assert(exp)
Declare that a condition should be true.
::epics::pvData::shared_vector< T > svector
std::tr1::shared_ptr< detail::SharedPut > put
A holder for a contiguous piece of memory.
#define FOREACH(ITERTYPE, IT, END, C)
StructureArrayConstPtr getStructureArray() const
void pdb_group_event(void *user_arg, struct dbChannel *chan, int eventsRemaining, struct db_field_log *pfl)
::epics::pvData::shared_vector< PVStructurePtr > svector
shared_ptr< T > static_pointer_cast(shared_ptr< U > const &r) BOOST_NOEXCEPT
virtual void replace(const const_svector &other) OVERRIDE FINAL
std::tr1::shared_ptr< const Structure > StructureConstPtr
::epics::pvData::shared_vector< const PVStructurePtr > const_svector
virtual void destroy() OVERRIDE FINAL
PVField is the base class for each PVData field.
void clear()
Clear contents. size() becomes 0.
std::tr1::shared_ptr< FieldBuilder > FieldBuilderPtr
static size_t num_instances
std::tr1::shared_ptr< PVDataCreate > PVDataCreatePtr
epics::pvData::PVStructurePtr complete
virtual epics::pvAccess::ChannelFind::shared_pointer channelFind(std::string const &channelName, epics::pvAccess::ChannelFindRequester::shared_pointer const &channelFindRequester) OVERRIDE FINAL
std::map< std::string, PDBPV::shared_pointer > persist_pv_map_t
bool operator<(shared_ptr< T > const &a, shared_ptr< U > const &b) BOOST_NOEXCEPT
void create(dbEventCtx ctx, dbChannel *ch, EVENTFUNC *fn, unsigned mask)
const ChannelProcessRequester::weak_pointer requester
void swap(shared_vector_base &o)
Swap the contents of this vector with another.
virtual std::string getProviderName() OVERRIDE FINAL
Data interface for a structure,.
bool operator!() const BOOST_NOEXCEPT
size_t size() const
Number of elements visible through this vector.
FORCE_INLINE const FieldCreatePtr & getFieldCreate()
epics::pvData::PVFieldPtr lookup(const epics::pvData::PVStructurePtr &S, epics::pvData::PVField **ppenclose) const
const Component & back() const
std::tr1::shared_ptr< PVStructure > PVStructurePtr
#define epicsThreadPriorityCAServerLow
Data class for a structureArray.
FORCE_INLINE std::tr1::shared_ptr< PVField > getSubFieldT(A a)
Class that holds the data for each possible scalar type.
std::tr1::shared_ptr< PVField > PVFieldPtr
std::tr1::shared_ptr< FieldCreate > FieldCreatePtr
virtual const_svector view() const OVERRIDE FINAL
Fetch a read-only view of the current array data.
epicsExportAddress(int, PDBProviderDebug)
static void parse(const char *txt, GroupConfig &result)
void swap(shared_ptr< T > &a, shared_ptr< T > &b) BOOST_NOEXCEPT
p2p::auto_ptr< PVIF > pvif
virtual epics::pvAccess::Channel::shared_pointer createChannel(std::string const &channelName, epics::pvAccess::ChannelRequester::shared_pointer const &channelRequester, short priority=PRIORITY_DEFAULT) OVERRIDE FINAL
std::tr1::shared_ptr< PVIFBuilder > builder
FORCE_INLINE const PVDataCreatePtr & getPVDataCreate()
PDBProvider(const epics::pvAccess::Configuration::const_shared_pointer &=epics::pvAccess::Configuration::const_shared_pointer())