pva2pva  1.4.1-dev
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
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  }
442 
443  if(!mem.structID.empty())
444  builder = builder->setId(mem.structID);
445 
446  DBCH chan;
447  if(!mem.pvname.empty()) {
448  DBCH temp(mem.pvname);
449  unsigned ftype = dbChannelFieldType(temp);
450 
451  // can't include in multi-locking
452  if(ftype>=DBF_INLINK && ftype<=DBF_FWDLINK)
453  throw std::runtime_error("Can't include link fields in group");
454 
455  chan.swap(temp);
456  }
457 
458  std::tr1::shared_ptr<PVIFBuilder> pvifbuilder(PVIFBuilder::create(mem.type, chan.chan));
459 
460  if(!parts.empty())
461  builder = pvifbuilder->dtype(builder, parts.back().name);
462  else
463  builder = pvifbuilder->dtype(builder, "");
464 
465  if(!parts.empty()) {
466  for(size_t j=0; j<parts.size()-1; j++)
467  builder = builder->endNested();
468  }
469 
470  if(!mem.pvname.empty()) {
471  members_map[mem.pvfldname] = J;
472  PDBGroupPV::Info& info = members[J];
473 
474  DBCH chan2;
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);
478  }
479 
480  info.allowProc = mem.putorder != std::numeric_limits<int>::min();
481  info.builder = PTRMOVE(pvifbuilder);
482  assert(info.builder.get());
483 
484  info.attachment.swap(parts);
485  info.chan.swap(chan);
486 
487  // info.triggers populated below
488 
489  assert(info.chan);
490  records[J] = dbChannelRecord(info.chan);
491 
492  J++;
493  }
494  }
495  pv->members.swap(members);
496 
497  pv->fielddesc = builder->createStructure();
498  pv->complete = pvbuilder->createPVStructure(pv->fielddesc);
499 
500  pv->complete->getSubFieldT<pvd::PVBoolean>("record._options.atomic")->put(pv->monatomic);
501 
502  DBManyLock L(&records[0], records.size(), 0);
503  pv->locker.swap(L);
504 
505  // construct locker for records triggered by each member
506  for(size_t i=0, J=0, N=info.members.size(); i<N; i++)
507  {
508  const GroupMemberInfo &mem = info.members[i];
509  if(mem.pvname.empty()) continue;
510  PDBGroupPV::Info& info = pv->members[J++];
511 
512  if(mem.triggers.empty()) continue;
513 
514  std::vector<dbCommon*> trig_records;
515  trig_records.reserve(mem.triggers.size());
516 
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");
521 
522  info.triggers.push_back(imap->second);
523  trig_records.push_back(records[imap->second]);
524  }
525 
526  DBManyLock L(&trig_records[0], trig_records.size(), 0);
527  info.locker.swap(L);
528  }
529 
530  persist_pv_map[info.name] = pv;
531 
532  }catch(std::exception& e){
533  fprintf(stderr, "%s: Error Group not created: %s\n", info.name.c_str(), e.what());
534  }
535  }
536 #else
537  if(!proc.groups.empty()) {
538  fprintf(stderr, "Group(s) were defined, but need Base >=3.16.0.2 to function. Ignoring.\n");
539  }
540 #endif // USE_MULTILOCK
541 
542  event_context = db_init_events();
543  if(!event_context)
544  throw std::runtime_error("Failed to create dbEvent context");
545  int ret = db_start_events(event_context, "PDB-event", NULL, NULL, epicsThreadPriorityCAServerLow-1);
546  if(ret!=DB_EVENT_OK)
547  throw std::runtime_error("Failed to stsart dbEvent context");
548 
549  // setup group monitors
550 #ifdef USE_MULTILOCK
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++)
555  {
556  const PDBPV::shared_pointer& ppv = it->second;
557  PDBGroupPV *pv = dynamic_cast<PDBGroupPV*>(ppv.get());
558  if(!pv)
559  continue;
560  try {
561 
562  // prepare for monitor
563 
564  size_t i=0;
565  FOREACH(PDBGroupPV::members_t::iterator, it2, end2, pv->members)
566  {
567  PDBGroupPV::Info& info = *it2;
568  info.evt_VALUE.index = info.evt_PROPERTY.index = i++;
569  info.evt_VALUE.self = info.evt_PROPERTY.self = pv;
570  assert(info.chan);
571 
572  info.pvif.reset(info.builder->attach(pv->complete, info.attachment));
573 
574  // TODO: don't need evt_PROPERTY for PVIF plain
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);
577 
578  if(!info.triggers.empty()) {
579  info.evt_VALUE.create(event_context, info.chan, &pdb_group_event, DBE_VALUE|DBE_ALARM);
580  }
581  }
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);
585  }
586  }
587 #endif // USE_MULTILOCK
588  epics::atomic::increment(num_instances);
589 }
590 
591 PDBProvider::~PDBProvider()
592 {
593  epics::atomic::decrement(num_instances);
594 
595  destroy();
596 }
597 
598 void PDBProvider::destroy()
599 {
600  dbEventCtx ctxt = NULL;
601 
602  persist_pv_map_t ppv;
603  {
604  epicsGuard<epicsMutex> G(transient_pv_map.mutex());
605  persist_pv_map.swap(ppv);
606  std::swap(ctxt, event_context);
607  }
608  ppv.clear(); // indirectly calls all db_cancel_events()
609  if(ctxt) db_close_events(ctxt);
610 }
611 
612 std::string PDBProvider::getProviderName() { return "QSRV"; }
613 
614 namespace {
615 struct ChannelFindRequesterNOOP : public pva::ChannelFind
616 {
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() {}
623 };
624 }
625 
626 pva::ChannelFind::shared_pointer
627 PDBProvider::channelFind(const std::string &channelName, const pva::ChannelFindRequester::shared_pointer &requester)
628 {
629  pva::ChannelFind::shared_pointer ret(new ChannelFindRequesterNOOP(shared_from_this()));
630 
631  bool found = false;
632  {
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)
637  found = true;
638  }
639  requester->channelFindResult(pvd::Status(), ret, found);
640  return ret;
641 }
642 
643 pva::ChannelFind::shared_pointer
644 PDBProvider::channelList(pva::ChannelListRequester::shared_pointer const & requester)
645 {
646  pva::ChannelFind::shared_pointer ret;
647  pvd::PVStringArray::svector names;
648  for(pdbRecordIterator rec; !rec.done(); rec.next())
649  {
650  names.push_back(rec.name());
651  }
652  {
653  epicsGuard<epicsMutex> G(transient_pv_map.mutex());
654 
655  for(persist_pv_map_t::const_iterator it=persist_pv_map.begin(), end=persist_pv_map.end();
656  it != end; ++it)
657  {
658  names.push_back(it->first);
659  }
660  }
661  // check for duplicates?
662  requester->channelListResult(pvd::Status::Ok,
663  shared_from_this(),
664  pvd::freeze(names), false);
665  return ret;
666 }
667 
668 pva::Channel::shared_pointer
669 PDBProvider::createChannel(std::string const & channelName,
670  pva::ChannelRequester::shared_pointer const & channelRequester,
671  short priority)
672 {
673  return createChannel(channelName, channelRequester, priority, "???");
674 }
675 
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)
680 {
681  pva::Channel::shared_pointer ret;
682  PDBPV::shared_pointer pv;
683  pvd::Status status;
684 
685  {
686  epicsGuard<epicsMutex> G(transient_pv_map.mutex());
687 
688  pv = transient_pv_map.find(channelName);
689  if(!pv) {
690  persist_pv_map_t::const_iterator it=persist_pv_map.find(channelName);
691  if(it!=persist_pv_map.end()) {
692  pv = it->second;
693  }
694  }
695  if(!pv) {
696  dbChannel *pchan = dbChannelCreate(channelName.c_str());
697  if(pchan) {
698  DBCH chan(pchan);
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);
702  spv->weakself = spv;
703  spv->activate();
704  }
705  }
706  }
707  if(pv) {
708  ret = pv->connect(shared_from_this(), requester);
709  }
710  if(!ret) {
711  status = pvd::Status(pvd::Status::STATUSTYPE_ERROR, "not found");
712  }
713  requester->channelCreated(status, ret);
714  return ret;
715 }
716 
717 FieldName::FieldName(const std::string& pv)
718 {
719  if(pv.empty())
720  return;
721  Splitter S(pv.c_str(), '.');
722  std::string part;
723  while(S.snip(part)) {
724  if(part.empty())
725  throw std::runtime_error("Empty field component in: "+pv);
726 
727  if(part[part.size()-1]==']') {
728  const size_t open = part.find_last_of('['),
729  N = part.size();
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';
735  }
736  if(!ok)
737  throw std::runtime_error("Invalid field array sub-script in : "+pv);
738 
739  parts.push_back(Component(part.substr(0, open), index));
740 
741  } else {
742  parts.push_back(Component(part));
743  }
744  }
745  if(parts.empty())
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);
749 }
750 
751 epics::pvData::PVFieldPtr
752 FieldName::lookup(const epics::pvData::PVStructurePtr& S, epics::pvData::PVField **ppsar) const
753 {
754  if(ppsar)
755  *ppsar = 0;
756 
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());
760  if(!parent)
761  throw std::runtime_error("mid-field is not structure");
762 
763  ret = parent->getSubFieldT(parts[i].name);
764 
765  if(parts[i].isArray()) {
766  pvd::PVStructureArray* sarr = dynamic_cast<pvd::PVStructureArray*>(ret.get());
767  if(!sarr)
768  throw std::runtime_error("indexed field is not structure array");
769 
770  if(ppsar && !*ppsar)
771  *ppsar = sarr;
772 
773  pvd::PVStructureArray::const_svector V(sarr->view());
774 
775  if(V.size()<=parts[i].index || !V[parts[i].index]) {
776  // automatic re-size and ensure non-null
777  V.clear(); // drop our extra ref so that reuse() might avoid a copy
778  pvd::PVStructureArray::svector E(sarr->reuse());
779 
780  if(E.size()<=parts[i].index)
781  E.resize(parts[i].index+1);
782 
783  if(!E[parts[i].index])
784  E[parts[i].index] = pvd::getPVDataCreate()->createPVStructure(sarr->getStructureArray()->getStructure());
785 
786  ret = E[parts[i].index];
787 
788  sarr->replace(pvd::freeze(E));
789 
790  } else {
791  ret = V[parts[i].index];
792  }
793  }
794  }
795  return ret;
796 }
797 
798 void FieldName::show() const
799 {
800  if(parts.empty()) {
801  printf("/");
802  return;
803  }
804 
805  bool first = true;
806  for(size_t i=0, N=parts.size(); i<N; i++)
807  {
808  if(!first) {
809  printf(".");
810  } else {
811  first = false;
812  }
813  if(parts[i].isArray())
814  printf("%s[%u]", parts[i].name.c_str(), (unsigned)parts[i].index);
815  else
816  printf("%s", parts[i].name.c_str());
817  }
818 }
819 
820 extern "C" {
821 epicsExportAddress(int, PDBProviderDebug);
822 }
Definition: pvif.h:81
Definition: conf.py:1