pva2pva  1.4.1
 All Classes Functions Variables Pages
configparse.cpp
1 
2 #include <sstream>
3 
4 #include <dbAccess.h>
5 
6 #include <dbAccess.h>
7 #include <dbChannel.h>
8 #include <dbStaticLib.h>
9 #include <dbEvent.h>
10 #include <dbLock.h>
11 
12 #include <pv/pvIntrospect.h>
13 #include <pv/pvAccess.h>
14 #include <pv/configuration.h>
15 #include <pv/json.h>
16 
17 #include "pdbgroup.h"
18 
19 namespace {
20 
21 namespace pvd = epics::pvData;
22 using pvd::yajl::integer_arg;
23 using pvd::yajl::size_arg;
24 
25 typedef std::map<std::string, pvd::AnyScalar> options_t;
26 typedef std::map<std::string, options_t> config_t;
27 
28 struct context {
29  const std::string chanprefix;
30  std::string msg;
31  std::string group, field, key;
32  unsigned depth; // number of '{'s
33  // depth 0 - invalid
34  // depth 1 - top Object
35  // depth 2 - Group
36  // depth 3 - field
37 
38  context(const std::string& chanprefix, GroupConfig& conf)
39  :chanprefix(chanprefix)
40  ,depth(0u)
41  ,conf(conf)
42  {}
43 
44  GroupConfig& conf;
45 
46  void can_assign()
47  {
48  if(depth<2 || depth>3)
49  throw std::runtime_error("Can't assign value in this context");
50  }
51 
52  void assign(const pvd::AnyScalar& value) {
53  can_assign();
54  GroupConfig::Group& grp = conf.groups[group];
55 
56  if(depth==2) {
57  if(field=="+atomic") {
58  grp.atomic = value.as<pvd::boolean>();
59  grp.atomic_set = true;
60 
61  } else if(field=="+id") {
62  grp.id = value.as<std::string>();
63 
64  } else {
65  conf.warning += "Unknown group option ";
66  conf.warning += field;
67  }
68  field.clear();
69 
70  } else if(depth==3) {
71  GroupConfig::Field& fld = grp.fields[field];
72 
73  if(key=="+type") {
74  fld.type = value.ref<std::string>();
75 
76  } else if(key=="+channel") {
77  fld.channel = chanprefix + value.ref<std::string>();
78 
79  } else if(key=="+id") {
80  fld.id = value.ref<std::string>();
81 
82  } else if(key=="+trigger") {
83  fld.trigger = value.ref<std::string>();
84 
85  } else if(key=="+putorder") {
86  fld.putorder = value.as<pvd::int32>();
87 
88  } else {
89  conf.warning += "Unknown group field option ";
90  conf.warning += field;
91  }
92  key.clear();
93  }
94  }
95 };
96 
97 #define TRY context *self = (context*)ctx; try
98 
99 #define CATCH() catch(std::exception& e) { if(self->msg.empty()) self->msg = e.what(); return 0; }
100 
101 int conf_null(void * ctx)
102 {
103  TRY {
104  self->assign(pvd::AnyScalar());
105  return 1;
106  }CATCH()
107 }
108 
109 
110 int conf_boolean(void * ctx, int boolVal)
111 {
112  TRY {
113  self->assign(pvd::AnyScalar(pvd::boolean(boolVal)));
114  return 1;
115  }CATCH()
116 }
117 
118 int conf_integer(void * ctx, integer_arg integerVal)
119 {
120  TRY {
121  self->assign(pvd::AnyScalar(pvd::int64(integerVal)));
122  return 1;
123  }CATCH()
124 }
125 
126 int conf_double(void * ctx, double doubleVal)
127 {
128  TRY {
129  self->assign(pvd::AnyScalar(doubleVal));
130  return 1;
131  }CATCH()
132 }
133 
134 int conf_string(void * ctx, const unsigned char * stringVal,
135  size_arg stringLen)
136 {
137  TRY {
138  std::string val((const char*)stringVal, stringLen);
139  self->assign(pvd::AnyScalar(val));
140  return 1;
141  }CATCH()
142 }
143 
144 int conf_start_map(void * ctx)
145 {
146  TRY {
147  self->depth++;
148  if(self->depth>3)
149  throw std::runtime_error("Group field def. can't contain Object (too deep)");
150  return 1;
151  }CATCH()
152 }
153 
154 int conf_map_key(void * ctx, const unsigned char * key,
155  size_arg stringLen)
156 {
157  TRY {
158  if(stringLen==0 && self->depth!=2)
159  throw std::runtime_error("empty group or key name not allowed");
160 
161  std::string name((const char*)key, stringLen);
162 
163  if(self->depth==1)
164  self->group.swap(name);
165  else if(self->depth==2)
166  self->field.swap(name);
167  else if(self->depth==3)
168  self->key.swap(name);
169  else
170  throw std::logic_error("Too deep!!");
171 
172  return 1;
173  }CATCH()
174 }
175 
176 int conf_end_map(void * ctx)
177 {
178  TRY {
179  assert(self->key.empty()); // cleared in assign()
180 
181  if(self->depth==3)
182  self->key.clear();
183  else if(self->depth==2)
184  self->field.clear();
185  else if(self->depth==1)
186  self->group.clear();
187  else
188  throw std::logic_error("Invalid depth");
189  self->depth--;
190 
191  return 1;
192  }CATCH()
193 }
194 
195 yajl_callbacks conf_cbs = {
196  &conf_null,
197  &conf_boolean,
198  &conf_integer,
199  &conf_double,
200  NULL, // number
201  &conf_string,
202  &conf_start_map,
203  &conf_map_key,
204  &conf_end_map,
205  NULL, // start_array,
206  NULL, // end_array,
207 };
208 
209 struct handler {
210  yajl_handle handle;
211  handler(yajl_handle handle) :handle(handle)
212  {
213  if(!handle)
214  throw std::runtime_error("Failed to allocate yajl handle");
215  }
216  ~handler() {
217  yajl_free(handle);
218  }
219  operator yajl_handle() { return handle; }
220 };
221 
222 }// namespace
223 
224 void GroupConfig::parse(const char *txt,
225  const char *recname,
226  GroupConfig& result)
227 {
228 #ifndef EPICS_YAJL_VERSION
229  yajl_parser_config conf;
230  memset(&conf, 0, sizeof(conf));
231  conf.allowComments = 1;
232  conf.checkUTF8 = 1;
233 #endif
234 
235  std::istringstream strm(txt);
236 
237  std::string chanprefix;
238  if(recname) {
239  chanprefix = recname;
240  chanprefix += '.';
241  }
242  context ctxt(chanprefix, result);
243 
244 #ifndef EPICS_YAJL_VERSION
245  handler handle(yajl_alloc(&conf_cbs, &conf, NULL, &ctxt));
246 #else
247  handler handle(yajl_alloc(&conf_cbs, NULL, &ctxt));
248 
249  yajl_config(handle, yajl_allow_comments, 1);
250 #endif
251 
252  if(!pvd::yajl_parse_helper(strm, handle))
253  throw std::runtime_error(ctxt.msg);
254 }