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;
32 namespace pva = epics::pvAccess;
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, NULL, 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);
441 if(parts.back().isArray()) {
442 builder = builder->addNestedStructureArray(parts.back().name);
446 if(!mem.structID.empty())
447 builder = builder->setId(mem.structID);
450 if(!mem.pvname.empty()) {
451 DBCH temp(mem.pvname);
452 unsigned ftype = dbChannelFieldType(temp);
455 if(ftype>=DBF_INLINK && ftype<=DBF_FWDLINK)
456 throw std::runtime_error(
"Can't include link fields in group");
461 std::tr1::shared_ptr<PVIFBuilder> pvifbuilder(PVIFBuilder::create(mem.type, chan.chan));
463 if(!parts.empty() && !parts.back().isArray())
464 builder = pvifbuilder->dtype(builder, parts.back().name);
466 builder = pvifbuilder->dtype(builder,
"");
469 for(
size_t j=0; j<parts.size()-1; j++)
470 builder = builder->endNested();
471 if(parts.back().isArray())
472 builder = builder->endNested();
475 if(!mem.pvname.empty()) {
476 members_map[mem.pvfldname] = J;
480 if(chan.chan && (ellCount(&chan.chan->pre_chain)>0 || ellCount(&chan.chan->post_chain)>0)) {
481 DBCH temp(mem.pvname);
482 info.chan2.swap(chan2);
485 info.allowProc = mem.putorder != std::numeric_limits<int>::min();
486 info.builder = PTRMOVE(pvifbuilder);
487 assert(info.builder.get());
489 info.attachment.swap(parts);
490 info.chan.swap(chan);
495 records[J] = dbChannelRecord(info.chan);
500 pv->members.swap(members);
502 pv->fielddesc = builder->createStructure();
503 pv->complete = pvbuilder->createPVStructure(pv->fielddesc);
505 pv->complete->getSubFieldT<pvd::PVBoolean>(
"record._options.atomic")->put(pv->monatomic);
507 DBManyLock L(&records[0], records.size(), 0);
511 for(
size_t i=0, J=0, N=info.members.size(); i<N; i++)
513 const GroupMemberInfo &mem = info.members[i];
514 if(mem.pvname.empty())
continue;
517 if(mem.triggers.empty())
continue;
519 std::vector<dbCommon*> trig_records;
520 trig_records.reserve(mem.triggers.size());
522 FOREACH(GroupMemberInfo::triggers_t::const_iterator, it, end, mem.triggers) {
523 members_map_t::const_iterator imap(members_map.find(*it));
524 if(imap==members_map.end())
525 throw std::logic_error(
"trigger resolution missed map to non-dbChannel");
527 info.triggers.push_back(imap->second);
528 trig_records.push_back(records[imap->second]);
531 DBManyLock L(&trig_records[0], trig_records.size(), 0);
535 persist_pv_map[info.name] = pv;
537 }
catch(std::exception& e){
538 fprintf(stderr,
"%s: Error Group not created: %s\n", info.name.c_str(), e.what());
542 if(!proc.groups.empty()) {
543 fprintf(stderr,
"Group(s) were defined, but need Base >=3.16.0.2 to function. Ignoring.\n");
545 #endif // USE_MULTILOCK
547 event_context = db_init_events();
549 throw std::runtime_error(
"Failed to create dbEvent context");
550 int ret = db_start_events(event_context,
"PDB-event", NULL, NULL, epicsThreadPriorityCAServerLow-1);
552 throw std::runtime_error(
"Failed to stsart dbEvent context");
556 for(persist_pv_map_t::iterator next = persist_pv_map.begin(),
557 end = persist_pv_map.end(),
558 it = next!=end ? next++ : end;
559 it != end; it = next==end ? end : next++)
561 const PDBPV::shared_pointer& ppv = it->second;
570 FOREACH(PDBGroupPV::members_t::iterator, it2, end2, pv->members)
573 info.evt_VALUE.index = info.evt_PROPERTY.index = i++;
574 info.evt_VALUE.self = info.evt_PROPERTY.self = pv;
577 info.pvif.reset(info.builder->attach(pv->complete, info.attachment));
580 dbChannel *pchan = info.chan2.chan ? info.chan2.chan : info.chan.chan;
581 info.evt_PROPERTY.create(event_context, pchan, &pdb_group_event, DBE_PROPERTY);
583 if(!info.triggers.empty()) {
584 info.evt_VALUE.create(event_context, info.chan, &pdb_group_event, DBE_VALUE|DBE_ALARM);
587 }
catch(std::exception& e){
588 fprintf(stderr,
"%s: Error during dbEvent setup : %s\n", pv->name.c_str(), e.what());
589 persist_pv_map.erase(it);
592 #endif // USE_MULTILOCK
593 epics::atomic::increment(num_instances);
596 PDBProvider::~PDBProvider()
598 epics::atomic::decrement(num_instances);
603 void PDBProvider::destroy()
605 dbEventCtx ctxt = NULL;
607 persist_pv_map_t ppv;
609 epicsGuard<epicsMutex> G(transient_pv_map.
mutex());
610 persist_pv_map.swap(ppv);
611 std::swap(ctxt, event_context);
614 if(ctxt) db_close_events(ctxt);
617 std::string PDBProvider::getProviderName() {
return "QSRV"; }
620 struct ChannelFindRequesterNOOP :
public pva::ChannelFind
622 const pva::ChannelProvider::weak_pointer provider;
623 ChannelFindRequesterNOOP(
const pva::ChannelProvider::shared_pointer& prov) : provider(prov) {}
624 virtual ~ChannelFindRequesterNOOP() {}
625 virtual void destroy() {}
626 virtual std::tr1::shared_ptr<pva::ChannelProvider> getChannelProvider() {
return provider.lock(); }
627 virtual void cancel() {}
631 pva::ChannelFind::shared_pointer
632 PDBProvider::channelFind(
const std::string &channelName,
const pva::ChannelFindRequester::shared_pointer &requester)
634 pva::ChannelFind::shared_pointer ret(
new ChannelFindRequesterNOOP(shared_from_this()));
638 epicsGuard<epicsMutex> G(transient_pv_map.
mutex());
639 if(persist_pv_map.find(channelName)!=persist_pv_map.end()
640 || transient_pv_map.
find(channelName)
641 || dbChannelTest(channelName.c_str())==0)
644 requester->channelFindResult(pvd::Status(), ret, found);
648 pva::ChannelFind::shared_pointer
649 PDBProvider::channelList(pva::ChannelListRequester::shared_pointer
const & requester)
651 pva::ChannelFind::shared_pointer ret;
652 pvd::PVStringArray::svector names;
655 names.push_back(rec.name());
658 epicsGuard<epicsMutex> G(transient_pv_map.
mutex());
660 for(persist_pv_map_t::const_iterator it=persist_pv_map.begin(), end=persist_pv_map.end();
663 names.push_back(it->first);
667 requester->channelListResult(pvd::Status::Ok,
669 pvd::freeze(names),
false);
673 pva::Channel::shared_pointer
674 PDBProvider::createChannel(std::string
const & channelName,
675 pva::ChannelRequester::shared_pointer
const & channelRequester,
678 return createChannel(channelName, channelRequester, priority,
"???");
681 pva::Channel::shared_pointer
682 PDBProvider::createChannel(std::string
const & channelName,
683 pva::ChannelRequester::shared_pointer
const & requester,
684 short priority, std::string
const & address)
686 pva::Channel::shared_pointer ret;
687 PDBPV::shared_pointer pv;
691 epicsGuard<epicsMutex> G(transient_pv_map.
mutex());
693 pv = transient_pv_map.
find(channelName);
695 persist_pv_map_t::const_iterator it=persist_pv_map.find(channelName);
696 if(it!=persist_pv_map.end()) {
701 dbChannel *pchan = dbChannelCreate(channelName.c_str());
704 pv.reset(
new PDBSinglePV(chan, shared_from_this()));
705 transient_pv_map.
insert(channelName, pv);
706 PDBSinglePV::shared_pointer spv = std::tr1::static_pointer_cast<
PDBSinglePV>(pv);
713 ret = pv->connect(shared_from_this(), requester);
716 status = pvd::Status(pvd::Status::STATUSTYPE_ERROR,
"not found");
718 requester->channelCreated(status, ret);
722 FieldName::FieldName(
const std::string& pv)
726 Splitter S(pv.c_str(),
'.');
728 while(S.snip(part)) {
730 throw std::runtime_error(
"Empty field component in: "+pv);
732 if(part[part.size()-1]==
']') {
733 const size_t open = part.find_last_of(
'['),
735 bool ok = open!=part.npos;
736 epicsUInt32 index = 0;
737 for(
size_t i=open+1; ok && i<(N-1); i++) {
738 ok &= part[i]>=
'0' && part[i]<=
'9';
739 index = 10*index + part[i] -
'0';
742 throw std::runtime_error(
"Invalid field array sub-script in : "+pv);
744 parts.push_back(Component(part.substr(0, open), index));
747 parts.push_back(Component(part));
751 throw std::runtime_error(
"Empty field name");
754 epics::pvData::PVFieldPtr
755 FieldName::lookup(
const epics::pvData::PVStructurePtr& S, epics::pvData::PVField **ppsar)
const
760 pvd::PVFieldPtr ret = S;
761 for(
size_t i=0, N=parts.size(); i<N; i++) {
762 pvd::PVStructure* parent =
dynamic_cast<pvd::PVStructure*
>(ret.get());
764 throw std::runtime_error(
"mid-field is not structure");
766 ret = parent->getSubFieldT(parts[i].name);
768 if(parts[i].isArray()) {
769 pvd::PVStructureArray* sarr =
dynamic_cast<pvd::PVStructureArray*
>(ret.get());
771 throw std::runtime_error(
"indexed field is not structure array");
776 pvd::PVStructureArray::const_svector V(sarr->view());
778 if(V.size()<=parts[i].index || !V[parts[i].index]) {
781 pvd::PVStructureArray::svector E(sarr->reuse());
783 if(E.size()<=parts[i].index)
784 E.resize(parts[i].index+1);
786 if(!E[parts[i].index])
787 E[parts[i].index] = pvd::getPVDataCreate()->createPVStructure(sarr->getStructureArray()->getStructure());
789 ret = E[parts[i].index];
791 sarr->replace(pvd::freeze(E));
794 ret = V[parts[i].index];
801 void FieldName::show()
const
809 for(
size_t i=0, N=parts.size(); i<N; i++)
816 if(parts[i].isArray())
817 printf(
"%s[%u]", parts[i].name.c_str(), (unsigned)parts[i].index);
819 printf(
"%s", parts[i].name.c_str());
824 epicsExportAddress(
int, PDBProviderDebug);
epicsMutex & mutex() const
value_pointer find(const K &k) const
value_pointer insert(const K &k, value_pointer &v)