6 #include <epicsString.h> 7 #include <epicsAtomic.h> 10 #include <epicsStdio.h> 13 #include <dbChannel.h> 14 #include <dbStaticLib.h> 19 #include <pv/pvAccess.h> 20 #include <pv/configuration.h> 23 #include "pdbsingle.h" 26 # include "pdbgroup.h" 29 #include <epicsExport.h> 31 namespace pvd = epics::pvData;
39 const char sep, *cur, *end;
40 Splitter(
const char *s,
char sep)
44 end = strchr(cur, sep);
46 bool operator!()
const {
return !cur; }
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 {
62 GroupMemberInfo() :putorder(0) {}
68 typedef std::set<std::string> triggers_t;
72 bool operator<(
const GroupMemberInfo& o)
const {
73 return putorder<o.putorder;
78 GroupInfo(
const std::string& name) : name(name),atomic(Unset),hastriggers(false) {}
79 std::string name, structID;
81 typedef std::vector<GroupMemberInfo> members_t;
84 typedef std::map<std::string, size_t> members_map_t;
85 members_map_t members_map;
87 typedef std::set<std::string> triggers_set_t;
88 typedef std::map<std::string, triggers_set_t> triggers_t;
91 enum tribool {Unset,True,False} atomic;
98 typedef std::map<std::string, GroupInfo> groups_t;
102 void resolveTriggers()
104 FOREACH(groups_t::iterator, it, end, groups) {
105 GroupInfo& info = it->second;
107 if(info.hastriggers) {
108 FOREACH(GroupInfo::triggers_t::iterator, it2, end2, info.triggers) {
109 const std::string& src = it2->first;
110 GroupInfo::triggers_set_t& targets = it2->second;
112 GroupInfo::members_map_t::iterator it2x = info.members_map.find(src);
113 if(it2x==info.members_map.end()) {
114 fprintf(stderr,
"Error: Group \"%s\" defines triggers from non-existant field \"%s\"\n",
115 info.name.c_str(), src.c_str());
118 GroupMemberInfo& srcmem = info.members[it2x->second];
120 if(PDBProviderDebug>2)
121 fprintf(stderr,
" pdb trg '%s.%s' -> ",
122 info.name.c_str(), src.c_str());
124 FOREACH(GroupInfo::triggers_set_t::const_iterator, it3, end3, targets) {
125 const std::string& target = *it3;
128 for(
size_t i=0; i<info.members.size(); i++) {
129 if(info.members[i].pvname.empty())
131 srcmem.triggers.insert(info.members[i].pvfldname);
132 if(PDBProviderDebug>2)
133 fprintf(stderr,
"%s, ", info.members[i].pvfldname.c_str());
138 GroupInfo::members_map_t::iterator it3x = info.members_map.find(target);
139 if(it3x==info.members_map.end()) {
140 fprintf(stderr,
"Error: Group \"%s\" defines triggers to non-existant field \"%s\"\n",
141 info.name.c_str(), target.c_str());
144 const GroupMemberInfo& targetmem = info.members[it3x->second];
146 if(targetmem.pvname.empty()) {
147 if(PDBProviderDebug>2)
148 fprintf(stderr,
"<ignore: %s>, ", targetmem.pvfldname.c_str());
152 srcmem.triggers.insert(targetmem.pvfldname);
153 if(PDBProviderDebug>2)
154 fprintf(stderr,
"%s, ", targetmem.pvfldname.c_str());
159 if(PDBProviderDebug>2) fprintf(stderr,
"\n");
162 if(PDBProviderDebug>1) fprintf(stderr,
" pdb default triggers for '%s'\n", info.name.c_str());
164 FOREACH(GroupInfo::members_t::iterator, it2, end2, info.members) {
165 GroupMemberInfo& mem = *it2;
166 if(mem.pvname.empty())
169 mem.triggers.insert(mem.pvfldname);
184 const char *json = rec.info(
"Q:group");
186 #ifndef USE_MULTILOCK 190 fprintf(stderr,
"%s: ignoring info(Q:Group, ...\n", rec.name());
193 if(PDBProviderDebug>2) {
194 fprintf(stderr,
"%s: info(Q:Group, ...\n", rec.name());
199 GroupConfig::parse(json, rec.name(), conf);
200 if(!conf.warning.empty())
201 fprintf(stderr,
"%s: warning(s) from info(Q:group, ...\n%s", rec.name(), conf.warning.c_str());
202 }
catch(std::exception& e){
203 fprintf(stderr,
"%s: Error parsing info(\"Q:group\", ... : %s\n",
204 rec.record()->name, e.what());
210 for(PDBProvider::group_files_t::const_iterator it(PDBProvider::group_files.begin()), end(PDBProvider::group_files.end());
213 std::ifstream jfile(it->c_str());
214 if(!jfile.is_open()) {
215 fprintf(stderr,
"Error opening \"%s\"\n", it->c_str());
219 std::vector<char> contents;
222 contents.resize(pos+1024u);
223 if(!jfile.read(&contents[pos], contents.size()-pos))
225 pos += jfile.gcount();
228 if(jfile.bad() || !jfile.eof()) {
229 fprintf(stderr,
"Error reading \"%s\"\n", it->c_str());
233 contents.push_back(
'\0');
234 const char *json = &contents[0];
236 if(PDBProviderDebug>2) {
237 fprintf(stderr,
"Process dbGroup file \"%s\"\n", it->c_str());
242 GroupConfig::parse(json,
"", conf);
243 if(!conf.warning.empty())
244 fprintf(stderr,
"warning(s) from dbGroup file \"%s\"\n%s", it->c_str(), conf.warning.c_str());
245 }
catch(std::exception& e){
246 fprintf(stderr,
"Error from dbGroup file \"%s\"\n%s", it->c_str(), e.what());
252 for(GroupConfig::groups_t::const_iterator git=conf.groups.begin(), gend=conf.groups.end();
255 const std::string& grpname = git->first;
259 if(dbChannelTest(grpname.c_str())==0) {
260 fprintf(stderr,
"%s : Error: Group name conflicts with record name. Ignoring...\n", grpname.c_str());
264 groups_t::iterator it = groups.find(grpname);
265 if(it==groups.end()) {
267 std::pair<groups_t::iterator, bool> ins(groups.insert(std::make_pair(grpname, GroupInfo(grpname))));
270 GroupInfo *curgroup = &it->second;
273 curgroup->structID = grp.id;
275 for(GroupConfig::Group::fields_t::const_iterator fit=grp.fields.begin(), fend=grp.fields.end();
278 const std::string& fldname = fit->first;
281 if(curgroup->members_map.find(fldname) != curgroup->members_map.end()) {
282 fprintf(stderr,
"%s.%s Warning: ignoring duplicate mapping %s\n",
283 grpname.c_str(), fldname.c_str(),
284 fld.channel.c_str());
288 curgroup->members.push_back(GroupMemberInfo());
289 GroupMemberInfo& info = curgroup->members.back();
290 info.pvname = fld.channel;
291 info.pvfldname = fldname;
292 info.structID = fld.id;
293 info.putorder = fld.putorder;
294 info.type = fld.type;
295 curgroup->members_map[fldname] = (size_t)-1;
297 if(PDBProviderDebug>2) {
298 fprintf(stderr,
" pdb map '%s.%s' <-> '%s'\n",
299 curgroup->name.c_str(),
300 curgroup->members.back().pvfldname.c_str(),
301 curgroup->members.back().pvname.c_str());
304 if(!fld.trigger.empty()) {
305 GroupInfo::triggers_t::iterator it = curgroup->triggers.find(fldname);
306 if(it==curgroup->triggers.end()) {
307 std::pair<GroupInfo::triggers_t::iterator, bool> ins(curgroup->triggers.insert(
308 std::make_pair(fldname, GroupInfo::triggers_set_t())));
312 Splitter sep(fld.trigger.c_str(),
',');
315 while(sep.snip(target)) {
316 curgroup->hastriggers =
true;
317 it->second.insert(target);
323 GroupInfo::tribool V = grp.atomic ? GroupInfo::True : GroupInfo::False;
325 if(curgroup->atomic!=GroupInfo::Unset && curgroup->atomic!=V)
326 fprintf(stderr,
"%s Warning: pdb atomic setting inconsistent '%s'\n",
327 grpname.c_str(), curgroup->name.c_str());
331 if(PDBProviderDebug>2)
332 fprintf(stderr,
" pdb atomic '%s' %s\n",
333 curgroup->name.c_str(), curgroup->atomic ?
"YES" :
"NO");
336 }
catch(std::exception& e){
337 fprintf(stderr,
"Error processing Q:group \"%s\" : %s\n",
338 grpname.c_str(), e.what());
344 for(groups_t::iterator it = groups.begin(), end = groups.end(); it!=end; ++it)
346 GroupInfo& info = it->second;
347 std::sort(info.members.begin(),
350 info.members_map.clear();
352 for(
size_t i=0, N=info.members.size(); i<N; i++)
354 info.members_map[info.members[i].pvfldname] = i;
367 size_t PDBProvider::num_instances;
369 std::list<std::string> PDBProvider::group_files;
371 PDBProvider::PDBProvider(
const epics::pvAccess::Configuration::const_shared_pointer &)
380 pvd::FieldCreatePtr fcreate(pvd::getFieldCreate());
381 pvd::PVDataCreatePtr pvbuilder(pvd::getPVDataCreate());
383 pvd::StructureConstPtr _options(fcreate->createFieldBuilder()
384 ->addNestedStructure(
"_options")
385 ->add(
"queueSize", pvd::pvUInt)
386 ->add(
"atomic", pvd::pvBoolean)
388 ->createStructure());
392 FOREACH(PDBProcessor::groups_t::const_iterator, it, end, proc.groups)
394 const GroupInfo &info=it->second;
396 if(persist_pv_map.find(info.name)!=persist_pv_map.end())
397 throw std::runtime_error(
"name already in used");
399 PDBGroupPV::shared_pointer pv(
new PDBGroupPV());
401 pv->name = info.name;
403 pv->pgatomic = info.atomic!=GroupInfo::False;
404 pv->monatomic = info.hastriggers;
407 pvd::shared_vector<PDBGroupPV::Info> members;
408 typedef std::map<std::string, size_t> members_map_t;
409 members_map_t members_map;
412 for(
size_t i=0, N=info.members.size(); i<N; i++)
413 if(!info.members[i].pvname.empty())
415 pvd::shared_vector<PDBGroupPV::Info> temp(nchans);
419 std::vector<dbCommon*> records(members.size());
421 pvd::FieldBuilderPtr builder(fcreate->createFieldBuilder());
422 builder = builder->add(
"record", _options);
424 if(!info.structID.empty())
425 builder = builder->setId(info.structID);
427 for(
size_t i=0, J=0, N=info.members.size(); i<N; i++)
429 const GroupMemberInfo &mem = info.members[i];
435 for(
size_t j=0; j<parts.size()-1; j++) {
436 if(parts[j].isArray())
437 builder = builder->addNestedStructureArray(parts[j].name);
439 builder = builder->addNestedStructure(parts[j].name);
443 if(!mem.structID.empty())
444 builder = builder->setId(mem.structID);
447 if(!mem.pvname.empty()) {
448 DBCH temp(mem.pvname);
449 unsigned ftype = dbChannelFieldType(temp);
452 if(ftype>=DBF_INLINK && ftype<=DBF_FWDLINK)
453 throw std::runtime_error(
"Can't include link fields in group");
458 std::tr1::shared_ptr<PVIFBuilder> pvifbuilder(PVIFBuilder::create(mem.type, chan.chan));
461 builder = pvifbuilder->dtype(builder, parts.back().name);
463 builder = pvifbuilder->dtype(builder,
"");
466 for(
size_t j=0; j<parts.size()-1; j++)
467 builder = builder->endNested();
470 if(!mem.pvname.empty()) {
471 members_map[mem.pvfldname] = J;
475 if(chan.chan && (ellCount(&chan.chan->pre_chain)>0 || ellCount(&chan.chan->post_chain)>0)) {
476 DBCH temp(mem.pvname);
477 info.chan2.swap(chan2);
480 info.allowProc = mem.putorder != std::numeric_limits<int>::min();
481 info.builder = PTRMOVE(pvifbuilder);
482 assert(info.builder.get());
484 info.attachment.swap(parts);
485 info.chan.swap(chan);
490 records[J] = dbChannelRecord(info.chan);
495 pv->members.swap(members);
497 pv->fielddesc = builder->createStructure();
498 pv->complete = pvbuilder->createPVStructure(pv->fielddesc);
500 pv->complete->getSubFieldT<pvd::PVBoolean>(
"record._options.atomic")->put(pv->monatomic);
502 DBManyLock L(&records[0], records.size(), 0);
506 for(
size_t i=0, J=0, N=info.members.size(); i<N; i++)
508 const GroupMemberInfo &mem = info.members[i];
509 if(mem.pvname.empty())
continue;
512 if(mem.triggers.empty())
continue;
514 std::vector<dbCommon*> trig_records;
515 trig_records.reserve(mem.triggers.size());
517 FOREACH(GroupMemberInfo::triggers_t::const_iterator, it, end, mem.triggers) {
518 members_map_t::const_iterator imap(members_map.find(*it));
519 if(imap==members_map.end())
520 throw std::logic_error(
"trigger resolution missed map to non-dbChannel");
522 info.triggers.push_back(imap->second);
523 trig_records.push_back(records[imap->second]);
526 DBManyLock L(&trig_records[0], trig_records.size(), 0);
530 persist_pv_map[info.name] = pv;
532 }
catch(std::exception& e){
533 fprintf(stderr,
"%s: Error Group not created: %s\n", info.name.c_str(), e.what());
537 if(!proc.groups.empty()) {
538 fprintf(stderr,
"Group(s) were defined, but need Base >=3.16.0.2 to function. Ignoring.\n");
540 #endif // USE_MULTILOCK 542 event_context = db_init_events();
544 throw std::runtime_error(
"Failed to create dbEvent context");
545 int ret = db_start_events(event_context,
"PDB-event", NULL, NULL, epicsThreadPriorityCAServerLow-1);
547 throw std::runtime_error(
"Failed to stsart dbEvent context");
551 for(persist_pv_map_t::iterator next = persist_pv_map.begin(),
552 end = persist_pv_map.end(),
553 it = next!=end ? next++ : end;
554 it != end; it = next==end ? end : next++)
556 const PDBPV::shared_pointer& ppv = it->second;
565 FOREACH(PDBGroupPV::members_t::iterator, it2, end2, pv->members)
568 info.evt_VALUE.index = info.evt_PROPERTY.index = i++;
569 info.evt_VALUE.self = info.evt_PROPERTY.self = pv;
572 info.pvif.reset(info.builder->attach(pv->complete, info.attachment));
575 dbChannel *pchan = info.chan2.chan ? info.chan2.chan : info.chan.chan;
576 info.evt_PROPERTY.create(event_context, pchan, &pdb_group_event, DBE_PROPERTY);
578 if(!info.triggers.empty()) {
579 info.evt_VALUE.create(event_context, info.chan, &pdb_group_event, DBE_VALUE|DBE_ALARM);
582 }
catch(std::exception& e){
583 fprintf(stderr,
"%s: Error during dbEvent setup : %s\n", pv->name.c_str(), e.what());
584 persist_pv_map.erase(it);
587 #endif // USE_MULTILOCK 588 epics::atomic::increment(num_instances);
591 PDBProvider::~PDBProvider()
593 epics::atomic::decrement(num_instances);
598 void PDBProvider::destroy()
600 dbEventCtx ctxt = NULL;
602 persist_pv_map_t ppv;
604 epicsGuard<epicsMutex> G(transient_pv_map.mutex());
605 persist_pv_map.swap(ppv);
606 std::swap(ctxt, event_context);
609 if(ctxt) db_close_events(ctxt);
612 std::string PDBProvider::getProviderName() {
return "QSRV"; }
615 struct ChannelFindRequesterNOOP :
public pva::ChannelFind
617 const pva::ChannelProvider::weak_pointer provider;
618 ChannelFindRequesterNOOP(
const pva::ChannelProvider::shared_pointer& prov) : provider(prov) {}
619 virtual ~ChannelFindRequesterNOOP() {}
620 virtual void destroy() {}
621 virtual std::tr1::shared_ptr<pva::ChannelProvider> getChannelProvider() {
return provider.lock(); }
622 virtual void cancel() {}
626 pva::ChannelFind::shared_pointer
627 PDBProvider::channelFind(
const std::string &channelName,
const pva::ChannelFindRequester::shared_pointer &requester)
629 pva::ChannelFind::shared_pointer ret(
new ChannelFindRequesterNOOP(shared_from_this()));
633 epicsGuard<epicsMutex> G(transient_pv_map.mutex());
634 if(persist_pv_map.find(channelName)!=persist_pv_map.end()
635 || transient_pv_map.find(channelName)
636 || dbChannelTest(channelName.c_str())==0)
639 requester->channelFindResult(pvd::Status(), ret, found);
643 pva::ChannelFind::shared_pointer
644 PDBProvider::channelList(pva::ChannelListRequester::shared_pointer
const & requester)
646 pva::ChannelFind::shared_pointer ret;
647 pvd::PVStringArray::svector names;
650 names.push_back(rec.name());
653 epicsGuard<epicsMutex> G(transient_pv_map.mutex());
655 for(persist_pv_map_t::const_iterator it=persist_pv_map.begin(), end=persist_pv_map.end();
658 names.push_back(it->first);
662 requester->channelListResult(pvd::Status::Ok,
664 pvd::freeze(names),
false);
668 pva::Channel::shared_pointer
669 PDBProvider::createChannel(std::string
const & channelName,
670 pva::ChannelRequester::shared_pointer
const & channelRequester,
673 return createChannel(channelName, channelRequester, priority,
"???");
676 pva::Channel::shared_pointer
677 PDBProvider::createChannel(std::string
const & channelName,
678 pva::ChannelRequester::shared_pointer
const & requester,
679 short priority, std::string
const & address)
681 pva::Channel::shared_pointer ret;
682 PDBPV::shared_pointer pv;
686 epicsGuard<epicsMutex> G(transient_pv_map.mutex());
688 pv = transient_pv_map.find(channelName);
690 persist_pv_map_t::const_iterator it=persist_pv_map.find(channelName);
691 if(it!=persist_pv_map.end()) {
696 dbChannel *pchan = dbChannelCreate(channelName.c_str());
699 pv.reset(
new PDBSinglePV(chan, shared_from_this()));
700 transient_pv_map.insert(channelName, pv);
701 PDBSinglePV::shared_pointer spv = std::tr1::static_pointer_cast<
PDBSinglePV>(pv);
708 ret = pv->connect(shared_from_this(), requester);
711 status = pvd::Status(pvd::Status::STATUSTYPE_ERROR,
"not found");
713 requester->channelCreated(status, ret);
717 FieldName::FieldName(
const std::string& pv)
721 Splitter S(pv.c_str(),
'.');
723 while(S.snip(part)) {
725 throw std::runtime_error(
"Empty field component in: "+pv);
727 if(part[part.size()-1]==
']') {
728 const size_t open = part.find_last_of(
'['),
730 bool ok = open!=part.npos;
731 epicsUInt32 index = 0;
732 for(
size_t i=open+1; ok && i<(N-1); i++) {
733 ok &= part[i]>=
'0' && part[i]<=
'9';
734 index = 10*index + part[i] -
'0';
737 throw std::runtime_error(
"Invalid field array sub-script in : "+pv);
739 parts.push_back(Component(part.substr(0, open), index));
742 parts.push_back(Component(part));
746 throw std::runtime_error(
"Empty field name");
747 if(parts.back().isArray())
748 throw std::runtime_error(
"leaf field may not have sub-script : "+pv);
751 epics::pvData::PVFieldPtr
752 FieldName::lookup(
const epics::pvData::PVStructurePtr& S, epics::pvData::PVField **ppsar)
const 757 pvd::PVFieldPtr ret = S;
758 for(
size_t i=0, N=parts.size(); i<N; i++) {
759 pvd::PVStructure* parent =
dynamic_cast<pvd::PVStructure*
>(ret.get());
761 throw std::runtime_error(
"mid-field is not structure");
763 ret = parent->getSubFieldT(parts[i].name);
765 if(parts[i].isArray()) {
766 pvd::PVStructureArray* sarr =
dynamic_cast<pvd::PVStructureArray*
>(ret.get());
768 throw std::runtime_error(
"indexed field is not structure array");
773 pvd::PVStructureArray::const_svector V(sarr->view());
775 if(V.size()<=parts[i].index || !V[parts[i].index]) {
778 pvd::PVStructureArray::svector E(sarr->reuse());
780 if(E.size()<=parts[i].index)
781 E.resize(parts[i].index+1);
783 if(!E[parts[i].index])
784 E[parts[i].index] = pvd::getPVDataCreate()->createPVStructure(sarr->getStructureArray()->getStructure());
786 ret = E[parts[i].index];
788 sarr->replace(pvd::freeze(E));
791 ret = V[parts[i].index];
798 void FieldName::show()
const 806 for(
size_t i=0, N=parts.size(); i<N; i++)
813 if(parts[i].isArray())
814 printf(
"%s[%u]", parts[i].name.c_str(), (unsigned)parts[i].index);
816 printf(
"%s", parts[i].name.c_str());
821 epicsExportAddress(
int, PDBProviderDebug);