pva2pva  1.4.1
 All Classes Functions Variables Pages
pdb.cpp
1 
2 #include <vector>
3 #include <utility>
4 
5 #include <errlog.h>
6 #include <epicsString.h>
7 #include <epicsAtomic.h>
8 
9 // printfs in this file will be redirected for capture
10 #include <epicsStdio.h>
11 
12 #include <dbAccess.h>
13 #include <dbChannel.h>
14 #include <dbStaticLib.h>
15 #include <dbNotify.h>
16 
17 #include <dbEvent.h>
18 
19 #include <pv/pvAccess.h>
20 #include <pv/configuration.h>
21 
22 #include "helper.h"
23 #include "pdbsingle.h"
24 #include "pvif.h"
25 #ifdef USE_MULTILOCK
26 # include "pdbgroup.h"
27 #endif
28 
29 #include <epicsExport.h>
30 
31 namespace pvd = epics::pvData;
32 namespace pva = epics::pvAccess;
33 
34 int PDBProviderDebug;
35 
36 namespace {
37 
38 struct Splitter {
39  const char sep, *cur, *end;
40  Splitter(const char *s, char sep)
41  :sep(sep), cur(s)
42  {
43  assert(s);
44  end = strchr(cur, sep);
45  }
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);
51  if(end) {
52  cur = end+1;
53  end = strchr(cur, sep);
54  } else {
55  cur = NULL;
56  }
57  return true;
58  }
59 };
60 
61 struct GroupMemberInfo {
62  GroupMemberInfo() :putorder(0) {}
63 
64  std::string pvname, // aka. name passed to dbChannelOpen()
65  pvfldname; // PVStructure sub-field
66  std::string structID; // ID to assign to sub-field
67  std::string type; // mapping type
68  typedef std::set<std::string> triggers_t;
69  triggers_t triggers; // names in GroupInfo::members_names which are post()d on events from pvfldname
70  int putorder;
71 
72  bool operator<(const GroupMemberInfo& o) const {
73  return putorder<o.putorder;
74  }
75 };
76 
77 struct GroupInfo {
78  GroupInfo(const std::string& name) : name(name),atomic(Unset),hastriggers(false) {}
79  std::string name, structID;
80 
81  typedef std::vector<GroupMemberInfo> members_t;
82  members_t members;
83 
84  typedef std::map<std::string, size_t> members_map_t;
85  members_map_t members_map;
86 
87  typedef std::set<std::string> triggers_set_t;
88  typedef std::map<std::string, triggers_set_t> triggers_t;
89  triggers_t triggers;
90 
91  enum tribool {Unset,True,False} atomic;
92  bool hastriggers;
93 };
94 
95 // Iterates all PDB records and gathers info() to construct PDB groups
96 struct PDBProcessor
97 {
98  typedef std::map<std::string, GroupInfo> groups_t;
99  groups_t groups;
100 
101  // validate trigger mappings and process into bit map form
102  void resolveTriggers()
103  {
104  FOREACH(groups_t::iterator, it, end, groups) { // for each group
105  GroupInfo& info = it->second;
106 
107  if(info.hastriggers) {
108  FOREACH(GroupInfo::triggers_t::iterator, it2, end2, info.triggers) { // for each trigger source
109  const std::string& src = it2->first;
110  GroupInfo::triggers_set_t& targets = it2->second;
111 
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());
116  continue;
117  }
118  GroupMemberInfo& srcmem = info.members[it2x->second];
119 
120  if(PDBProviderDebug>2)
121  fprintf(stderr, " pdb trg '%s.%s' -> ",
122  info.name.c_str(), src.c_str());
123 
124  FOREACH(GroupInfo::triggers_set_t::const_iterator, it3, end3, targets) { // for each trigger target
125  const std::string& target = *it3;
126 
127  if(target=="*") {
128  for(size_t i=0; i<info.members.size(); i++) {
129  if(info.members[i].pvname.empty())
130  continue;
131  srcmem.triggers.insert(info.members[i].pvfldname);
132  if(PDBProviderDebug>2)
133  fprintf(stderr, "%s, ", info.members[i].pvfldname.c_str());
134  }
135 
136  } else {
137 
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());
142  continue;
143  }
144  const GroupMemberInfo& targetmem = info.members[it3x->second];
145 
146  if(targetmem.pvname.empty()) {
147  if(PDBProviderDebug>2)
148  fprintf(stderr, "<ignore: %s>, ", targetmem.pvfldname.c_str());
149 
150  } else {
151  // and finally, update source BitSet
152  srcmem.triggers.insert(targetmem.pvfldname);
153  if(PDBProviderDebug>2)
154  fprintf(stderr, "%s, ", targetmem.pvfldname.c_str());
155  }
156  }
157  }
158 
159  if(PDBProviderDebug>2) fprintf(stderr, "\n");
160  }
161  } else {
162  if(PDBProviderDebug>1) fprintf(stderr, " pdb default triggers for '%s'\n", info.name.c_str());
163 
164  FOREACH(GroupInfo::members_t::iterator, it2, end2, info.members) {
165  GroupMemberInfo& mem = *it2;
166  if(mem.pvname.empty())
167  continue;
168 
169  mem.triggers.insert(mem.pvfldname); // default is self trigger
170  }
171  }
172  }
173  }
174 
175  PDBProcessor()
176  {
177 #ifdef USE_MULTILOCK
178  GroupConfig conf;
179 #endif
180 
181  // process info(Q:Group, ...)
182  for(pdbRecordIterator rec; !rec.done(); rec.next())
183  {
184  const char *json = rec.info("Q:group");
185  if(!json) continue;
186 #ifndef USE_MULTILOCK
187  static bool warned;
188  if(!warned) {
189  warned = true;
190  fprintf(stderr, "%s: ignoring info(Q:Group, ...\n", rec.name());
191  }
192 #endif
193  if(PDBProviderDebug>2) {
194  fprintf(stderr, "%s: info(Q:Group, ...\n", rec.name());
195  }
196 
197 #ifdef USE_MULTILOCK
198  try {
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());
205  }
206 #endif
207  }
208 
209  // process group definition files
210  for(PDBProvider::group_files_t::const_iterator it(PDBProvider::group_files.begin()), end(PDBProvider::group_files.end());
211  it != end; ++it)
212  {
213  std::ifstream jfile(it->c_str());
214  if(!jfile.is_open()) {
215  fprintf(stderr, "Error opening \"%s\"\n", it->c_str());
216  continue;
217  }
218 
219  std::vector<char> contents;
220  size_t pos=0u;
221  while(true) {
222  contents.resize(pos+1024u);
223  if(!jfile.read(&contents[pos], contents.size()-pos))
224  break;
225  pos += jfile.gcount();
226  }
227 
228  if(jfile.bad() || !jfile.eof()) {
229  fprintf(stderr, "Error reading \"%s\"\n", it->c_str());
230  continue;
231  }
232 
233  contents.push_back('\0');
234  const char *json = &contents[0];
235 
236  if(PDBProviderDebug>2) {
237  fprintf(stderr, "Process dbGroup file \"%s\"\n", it->c_str());
238  }
239 
240 #ifdef USE_MULTILOCK
241  try {
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());
247  }
248 #endif
249  }
250 
251 #ifdef USE_MULTILOCK
252  for(GroupConfig::groups_t::const_iterator git=conf.groups.begin(), gend=conf.groups.end();
253  git!=gend; ++git)
254  {
255  const std::string& grpname = git->first;
256  const GroupConfig::Group& grp = git->second;
257  try {
258 
259  if(dbChannelTest(grpname.c_str())==0) {
260  fprintf(stderr, "%s : Error: Group name conflicts with record name. Ignoring...\n", grpname.c_str());
261  continue;
262  }
263 
264  groups_t::iterator it = groups.find(grpname);
265  if(it==groups.end()) {
266  // lazy creation of group
267  std::pair<groups_t::iterator, bool> ins(groups.insert(std::make_pair(grpname, GroupInfo(grpname))));
268  it = ins.first;
269  }
270  GroupInfo *curgroup = &it->second;
271 
272  if(!grp.id.empty())
273  curgroup->structID = grp.id;
274 
275  for(GroupConfig::Group::fields_t::const_iterator fit=grp.fields.begin(), fend=grp.fields.end();
276  fit!=fend; ++fit)
277  {
278  const std::string& fldname = fit->first;
279  const GroupConfig::Field& fld = fit->second;
280 
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());
285  continue;
286  }
287 
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; // placeholder see below
296 
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());
302  }
303 
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())));
309  it = ins.first;
310  }
311 
312  Splitter sep(fld.trigger.c_str(), ',');
313  std::string target;
314 
315  while(sep.snip(target)) {
316  curgroup->hastriggers = true;
317  it->second.insert(target);
318  }
319  }
320  }
321 
322  if(grp.atomic_set) {
323  GroupInfo::tribool V = grp.atomic ? GroupInfo::True : GroupInfo::False;
324 
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());
328 
329  curgroup->atomic=V;
330 
331  if(PDBProviderDebug>2)
332  fprintf(stderr, " pdb atomic '%s' %s\n",
333  curgroup->name.c_str(), curgroup->atomic ? "YES" : "NO");
334  }
335 
336  }catch(std::exception& e){
337  fprintf(stderr, "Error processing Q:group \"%s\" : %s\n",
338  grpname.c_str(), e.what());
339  }
340  }
341 
342  // re-sort GroupInfo::members to ensure the shorter names appear first
343  // allows use of 'existing' PVIFBuilder on leaves.
344  for(groups_t::iterator it = groups.begin(), end = groups.end(); it!=end; ++it)
345  {
346  GroupInfo& info = it->second;
347  std::sort(info.members.begin(),
348  info.members.end());
349 
350  info.members_map.clear();
351 
352  for(size_t i=0, N=info.members.size(); i<N; i++)
353  {
354  info.members_map[info.members[i].pvfldname] = i;
355  }
356  }
357 
358  resolveTriggers();
359  // must not re-sort members after this point as resolveTriggers()
360  // has stored array indicies.
361 #endif
362  }
363 
364 };
365 }
366 
367 size_t PDBProvider::num_instances;
368 
369 std::list<std::string> PDBProvider::group_files;
370 
371 PDBProvider::PDBProvider(const epics::pvAccess::Configuration::const_shared_pointer &)
372 {
373  /* Long view
374  * 1. PDBProcessor collects info() tags and builds config of groups and group fields
375  * (including those w/o a dbChannel)
376  * 2. Build pvd::Structure and discard those w/o dbChannel
377  * 3. Build the lockers for the triggers of each group field
378  */
379  PDBProcessor proc;
380  pvd::FieldCreatePtr fcreate(pvd::getFieldCreate());
381  pvd::PVDataCreatePtr pvbuilder(pvd::getPVDataCreate());
382 
383  pvd::StructureConstPtr _options(fcreate->createFieldBuilder()
384  ->addNestedStructure("_options")
385  ->add("queueSize", pvd::pvUInt)
386  ->add("atomic", pvd::pvBoolean)
387  ->endNested()
388  ->createStructure());
389 
390 #ifdef USE_MULTILOCK
391  // assemble group PVD structure definitions and build dbLockers
392  FOREACH(PDBProcessor::groups_t::const_iterator, it, end, proc.groups)
393  {
394  const GroupInfo &info=it->second;
395  try{
396  if(persist_pv_map.find(info.name)!=persist_pv_map.end())
397  throw std::runtime_error("name already in used");
398 
399  PDBGroupPV::shared_pointer pv(new PDBGroupPV());
400  pv->weakself = pv;
401  pv->name = info.name;
402 
403  pv->pgatomic = info.atomic!=GroupInfo::False; // default true if Unset
404  pv->monatomic = info.hastriggers;
405 
406  // some gymnastics because Info isn't copyable
407  pvd::shared_vector<PDBGroupPV::Info> members;
408  typedef std::map<std::string, size_t> members_map_t;
409  members_map_t members_map;
410  {
411  size_t nchans = 0;
412  for(size_t i=0, N=info.members.size(); i<N; i++)
413  if(!info.members[i].pvname.empty())
414  nchans++;
415  pvd::shared_vector<PDBGroupPV::Info> temp(nchans);
416  members.swap(temp);
417  }
418 
419  std::vector<dbCommon*> records(members.size());
420 
421  pvd::FieldBuilderPtr builder(fcreate->createFieldBuilder());
422  builder = builder->add("record", _options);
423 
424  if(!info.structID.empty())
425  builder = builder->setId(info.structID);
426 
427  for(size_t i=0, J=0, N=info.members.size(); i<N; i++)
428  {
429  const GroupMemberInfo &mem = info.members[i];
430 
431  // parse down attachment point to build/traverse structure
432  FieldName parts(mem.pvfldname);
433 
434  if(!parts.empty()) {
435  for(size_t j=0; j<parts.size()-1; j++) {
436  if(parts[j].isArray())
437  builder = builder->addNestedStructureArray(parts[j].name);
438  else
439  builder = builder->addNestedStructure(parts[j].name);
440  }
441  if(parts.back().isArray()) {
442  builder = builder->addNestedStructureArray(parts.back().name);
443  }
444  }
445 
446  if(!mem.structID.empty())
447  builder = builder->setId(mem.structID);
448 
449  DBCH chan;
450  if(!mem.pvname.empty()) {
451  DBCH temp(mem.pvname);
452  unsigned ftype = dbChannelFieldType(temp);
453 
454  // can't include in multi-locking
455  if(ftype>=DBF_INLINK && ftype<=DBF_FWDLINK)
456  throw std::runtime_error("Can't include link fields in group");
457 
458  chan.swap(temp);
459  }
460 
461  std::tr1::shared_ptr<PVIFBuilder> pvifbuilder(PVIFBuilder::create(mem.type, chan.chan));
462 
463  if(!parts.empty() && !parts.back().isArray())
464  builder = pvifbuilder->dtype(builder, parts.back().name);
465  else
466  builder = pvifbuilder->dtype(builder, "");
467 
468  if(!parts.empty()) {
469  for(size_t j=0; j<parts.size()-1; j++)
470  builder = builder->endNested();
471  if(parts.back().isArray())
472  builder = builder->endNested();
473  }
474 
475  if(!mem.pvname.empty()) {
476  members_map[mem.pvfldname] = J;
477  PDBGroupPV::Info& info = members[J];
478 
479  DBCH chan2;
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);
483  }
484 
485  info.allowProc = mem.putorder != std::numeric_limits<int>::min();
486  info.builder = PTRMOVE(pvifbuilder);
487  assert(info.builder.get());
488 
489  info.attachment.swap(parts);
490  info.chan.swap(chan);
491 
492  // info.triggers populated below
493 
494  assert(info.chan);
495  records[J] = dbChannelRecord(info.chan);
496 
497  J++;
498  }
499  }
500  pv->members.swap(members);
501 
502  pv->fielddesc = builder->createStructure();
503  pv->complete = pvbuilder->createPVStructure(pv->fielddesc);
504 
505  pv->complete->getSubFieldT<pvd::PVBoolean>("record._options.atomic")->put(pv->monatomic);
506 
507  DBManyLock L(&records[0], records.size(), 0);
508  pv->locker.swap(L);
509 
510  // construct locker for records triggered by each member
511  for(size_t i=0, J=0, N=info.members.size(); i<N; i++)
512  {
513  const GroupMemberInfo &mem = info.members[i];
514  if(mem.pvname.empty()) continue;
515  PDBGroupPV::Info& info = pv->members[J++];
516 
517  if(mem.triggers.empty()) continue;
518 
519  std::vector<dbCommon*> trig_records;
520  trig_records.reserve(mem.triggers.size());
521 
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");
526 
527  info.triggers.push_back(imap->second);
528  trig_records.push_back(records[imap->second]);
529  }
530 
531  DBManyLock L(&trig_records[0], trig_records.size(), 0);
532  info.locker.swap(L);
533  }
534 
535  persist_pv_map[info.name] = pv;
536 
537  }catch(std::exception& e){
538  fprintf(stderr, "%s: Error Group not created: %s\n", info.name.c_str(), e.what());
539  }
540  }
541 #else
542  if(!proc.groups.empty()) {
543  fprintf(stderr, "Group(s) were defined, but need Base >=3.16.0.2 to function. Ignoring.\n");
544  }
545 #endif // USE_MULTILOCK
546 
547  event_context = db_init_events();
548  if(!event_context)
549  throw std::runtime_error("Failed to create dbEvent context");
550  int ret = db_start_events(event_context, "PDB-event", NULL, NULL, epicsThreadPriorityCAServerLow-1);
551  if(ret!=DB_EVENT_OK)
552  throw std::runtime_error("Failed to stsart dbEvent context");
553 
554  // setup group monitors
555 #ifdef USE_MULTILOCK
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++)
560  {
561  const PDBPV::shared_pointer& ppv = it->second;
562  PDBGroupPV *pv = dynamic_cast<PDBGroupPV*>(ppv.get());
563  if(!pv)
564  continue;
565  try {
566 
567  // prepare for monitor
568 
569  size_t i=0;
570  FOREACH(PDBGroupPV::members_t::iterator, it2, end2, pv->members)
571  {
572  PDBGroupPV::Info& info = *it2;
573  info.evt_VALUE.index = info.evt_PROPERTY.index = i++;
574  info.evt_VALUE.self = info.evt_PROPERTY.self = pv;
575  assert(info.chan);
576 
577  info.pvif.reset(info.builder->attach(pv->complete, info.attachment));
578 
579  // TODO: don't need evt_PROPERTY for PVIF plain
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);
582 
583  if(!info.triggers.empty()) {
584  info.evt_VALUE.create(event_context, info.chan, &pdb_group_event, DBE_VALUE|DBE_ALARM);
585  }
586  }
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);
590  }
591  }
592 #endif // USE_MULTILOCK
593  epics::atomic::increment(num_instances);
594 }
595 
596 PDBProvider::~PDBProvider()
597 {
598  epics::atomic::decrement(num_instances);
599 
600  destroy();
601 }
602 
603 void PDBProvider::destroy()
604 {
605  dbEventCtx ctxt = NULL;
606 
607  persist_pv_map_t ppv;
608  {
609  epicsGuard<epicsMutex> G(transient_pv_map.mutex());
610  persist_pv_map.swap(ppv);
611  std::swap(ctxt, event_context);
612  }
613  ppv.clear(); // indirectly calls all db_cancel_events()
614  if(ctxt) db_close_events(ctxt);
615 }
616 
617 std::string PDBProvider::getProviderName() { return "QSRV"; }
618 
619 namespace {
620 struct ChannelFindRequesterNOOP : public pva::ChannelFind
621 {
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() {}
628 };
629 }
630 
631 pva::ChannelFind::shared_pointer
632 PDBProvider::channelFind(const std::string &channelName, const pva::ChannelFindRequester::shared_pointer &requester)
633 {
634  pva::ChannelFind::shared_pointer ret(new ChannelFindRequesterNOOP(shared_from_this()));
635 
636  bool found = false;
637  {
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)
642  found = true;
643  }
644  requester->channelFindResult(pvd::Status(), ret, found);
645  return ret;
646 }
647 
648 pva::ChannelFind::shared_pointer
649 PDBProvider::channelList(pva::ChannelListRequester::shared_pointer const & requester)
650 {
651  pva::ChannelFind::shared_pointer ret;
652  pvd::PVStringArray::svector names;
653  for(pdbRecordIterator rec; !rec.done(); rec.next())
654  {
655  names.push_back(rec.name());
656  }
657  {
658  epicsGuard<epicsMutex> G(transient_pv_map.mutex());
659 
660  for(persist_pv_map_t::const_iterator it=persist_pv_map.begin(), end=persist_pv_map.end();
661  it != end; ++it)
662  {
663  names.push_back(it->first);
664  }
665  }
666  // check for duplicates?
667  requester->channelListResult(pvd::Status::Ok,
668  shared_from_this(),
669  pvd::freeze(names), false);
670  return ret;
671 }
672 
673 pva::Channel::shared_pointer
674 PDBProvider::createChannel(std::string const & channelName,
675  pva::ChannelRequester::shared_pointer const & channelRequester,
676  short priority)
677 {
678  return createChannel(channelName, channelRequester, priority, "???");
679 }
680 
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)
685 {
686  pva::Channel::shared_pointer ret;
687  PDBPV::shared_pointer pv;
688  pvd::Status status;
689 
690  {
691  epicsGuard<epicsMutex> G(transient_pv_map.mutex());
692 
693  pv = transient_pv_map.find(channelName);
694  if(!pv) {
695  persist_pv_map_t::const_iterator it=persist_pv_map.find(channelName);
696  if(it!=persist_pv_map.end()) {
697  pv = it->second;
698  }
699  }
700  if(!pv) {
701  dbChannel *pchan = dbChannelCreate(channelName.c_str());
702  if(pchan) {
703  DBCH chan(pchan);
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);
707  spv->weakself = spv;
708  spv->activate();
709  }
710  }
711  }
712  if(pv) {
713  ret = pv->connect(shared_from_this(), requester);
714  }
715  if(!ret) {
716  status = pvd::Status(pvd::Status::STATUSTYPE_ERROR, "not found");
717  }
718  requester->channelCreated(status, ret);
719  return ret;
720 }
721 
722 FieldName::FieldName(const std::string& pv)
723 {
724  if(pv.empty())
725  return;
726  Splitter S(pv.c_str(), '.');
727  std::string part;
728  while(S.snip(part)) {
729  if(part.empty())
730  throw std::runtime_error("Empty field component in: "+pv);
731 
732  if(part[part.size()-1]==']') {
733  const size_t open = part.find_last_of('['),
734  N = part.size();
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';
740  }
741  if(!ok)
742  throw std::runtime_error("Invalid field array sub-script in : "+pv);
743 
744  parts.push_back(Component(part.substr(0, open), index));
745 
746  } else {
747  parts.push_back(Component(part));
748  }
749  }
750  if(parts.empty())
751  throw std::runtime_error("Empty field name");
752 }
753 
754 epics::pvData::PVFieldPtr
755 FieldName::lookup(const epics::pvData::PVStructurePtr& S, epics::pvData::PVField **ppsar) const
756 {
757  if(ppsar)
758  *ppsar = 0;
759 
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());
763  if(!parent)
764  throw std::runtime_error("mid-field is not structure");
765 
766  ret = parent->getSubFieldT(parts[i].name);
767 
768  if(parts[i].isArray()) {
769  pvd::PVStructureArray* sarr = dynamic_cast<pvd::PVStructureArray*>(ret.get());
770  if(!sarr)
771  throw std::runtime_error("indexed field is not structure array");
772 
773  if(ppsar && !*ppsar)
774  *ppsar = sarr;
775 
776  pvd::PVStructureArray::const_svector V(sarr->view());
777 
778  if(V.size()<=parts[i].index || !V[parts[i].index]) {
779  // automatic re-size and ensure non-null
780  V.clear(); // drop our extra ref so that reuse() might avoid a copy
781  pvd::PVStructureArray::svector E(sarr->reuse());
782 
783  if(E.size()<=parts[i].index)
784  E.resize(parts[i].index+1);
785 
786  if(!E[parts[i].index])
787  E[parts[i].index] = pvd::getPVDataCreate()->createPVStructure(sarr->getStructureArray()->getStructure());
788 
789  ret = E[parts[i].index];
790 
791  sarr->replace(pvd::freeze(E));
792 
793  } else {
794  ret = V[parts[i].index];
795  }
796  }
797  }
798  return ret;
799 }
800 
801 void FieldName::show() const
802 {
803  if(parts.empty()) {
804  printf("/");
805  return;
806  }
807 
808  bool first = true;
809  for(size_t i=0, N=parts.size(); i<N; i++)
810  {
811  if(!first) {
812  printf(".");
813  } else {
814  first = false;
815  }
816  if(parts[i].isArray())
817  printf("%s[%u]", parts[i].name.c_str(), (unsigned)parts[i].index);
818  else
819  printf("%s", parts[i].name.c_str());
820  }
821 }
822 
823 extern "C" {
824 epicsExportAddress(int, PDBProviderDebug);
825 }
Definition: pvif.h:81
epicsMutex & mutex() const
Definition: weakmap.h:276
value_pointer find(const K &k) const
Definition: weakmap.h:215
value_pointer insert(const K &k, value_pointer &v)
Definition: weakmap.h:230