pva2pva  1.4.1
 All Classes Functions Variables Pages
gwmain.cpp
1 
2 #include <iostream>
3 #include <fstream>
4 #include <stdexcept>
5 #include <map>
6 
7 #if !defined(_WIN32)
8 #include <signal.h>
9 #define USE_SIGNAL
10 #endif
11 
12 #include <epicsStdlib.h>
13 #include <epicsGetopt.h>
14 #include <iocsh.h>
15 #include <epicsTimer.h>
16 #include <libComRegister.h>
17 
18 #include <pv/json.h>
19 
20 #include <pv/pvAccess.h>
21 #include <pv/clientFactory.h>
22 #include <pv/configuration.h>
23 #include <pv/serverContext.h>
24 #include <pv/reftrack.h>
25 #include <pv/iocreftrack.h>
26 #include <pv/iocshelper.h>
27 #include <pv/logger.h>
28 
29 #include "server.h"
30 #include "pva2pva.h"
31 
32 namespace pvd = epics::pvData;
33 namespace pva = epics::pvAccess;
34 
35 extern int p2pReadOnly;
36 
37 namespace {
38 
39 pvd::StructureConstPtr schema(pvd::getFieldCreate()->createFieldBuilder()
40  ->add("version", pvd::pvUInt)
41  ->add("readOnly", pvd::pvBoolean)
42  ->addNestedStructureArray("clients")
43  ->add("name", pvd::pvString)
44  ->add("provider", pvd::pvString)
45  ->add("addrlist", pvd::pvString)
46  ->add("autoaddrlist", pvd::pvBoolean)
47  ->add("serverport", pvd::pvUShort)
48  ->add("bcastport", pvd::pvUShort)
49  ->endNested()
50  ->addNestedStructureArray("servers")
51  ->add("name", pvd::pvString)
52  ->addArray("clients", pvd::pvString)
53  ->add("interface", pvd::pvString)
54  ->add("addrlist", pvd::pvString)
55  ->add("autoaddrlist", pvd::pvBoolean)
56  ->add("serverport", pvd::pvUShort)
57  ->add("bcastport", pvd::pvUShort)
58  ->add("control_prefix", pvd::pvString)
59  ->endNested()
60  ->createStructure());
61 
62 
63 void usage(const char *me)
64 {
65  std::cerr<<"Usage: "<<me<<" [-vhiIC] <config file>\n";
66 }
67 
68 void getargs(ServerConfig& arg, int argc, char *argv[])
69 {
70  int opt;
71  bool checkonly = false;
72 
73  while( (opt=getopt(argc, argv, "qvhiIC"))!=-1)
74  {
75  switch(opt) {
76  case 'q':
77  arg.debug--;
78  break;
79  case 'v':
80  arg.debug++;
81  break;
82  case 'I':
83  arg.interactive = true;
84  break;
85  case 'i':
86  arg.interactive = false;
87  break;
88  case 'C':
89  checkonly = true;
90  break;
91  default:
92  std::cerr<<"Unknown argument -"<<char(opt)<<"\n";
93  case 'h':
94  usage(argv[0]);
95  exit(1);
96  }
97  }
98 
99  if(optind!=argc-1) {
100  std::cerr<<"Exactly one positional argument expected\n";
101  usage(argv[0]);
102  exit(1);
103  }
104 
105  arg.conf = pvd::getPVDataCreate()->createPVStructure(schema);
106  std::ifstream strm(argv[optind]);
107  pvd::parseJSON(strm, arg.conf);
108 
109  p2pReadOnly = arg.conf->getSubFieldT<pvd::PVScalar>("readOnly")->getAs<pvd::boolean>();
110 
111  unsigned version = arg.conf->getSubFieldT<pvd::PVUInt>("version")->get();
112  if(version==0) {
113  std::cerr<<"Warning: config file missing \"version\" key. Assuming 1\n";
114  } else if(version!=1) {
115  std::cerr<<"config file version mis-match. expect 1 found "<<version<<"\n";
116  exit(1);
117  }
118  if(arg.conf->getSubFieldT<pvd::PVStructureArray>("clients")->view().empty()) {
119  std::cerr<<"No clients configured\n";
120  exit(1);
121  }
122  if(arg.conf->getSubFieldT<pvd::PVStructureArray>("servers")->view().empty()) {
123  std::cerr<<"No servers configured\n";
124  exit(1);
125  }
126 
127  if(checkonly) {
128  std::cerr<<"Config file OK\n";
129  exit(0);
130  }
131 }
132 
133 GWServerChannelProvider::shared_pointer configure_client(ServerConfig& arg, const pvd::PVStructurePtr& conf)
134 {
135  std::string name(conf->getSubFieldT<pvd::PVString>("name")->get());
136  std::string provider(conf->getSubFieldT<pvd::PVString>("provider")->get());
137 
138  LOG(pva::logLevelInfo, "Configure client '%s' with provider '%s'", name.c_str(), provider.c_str());
139 
140  pva::Configuration::shared_pointer C(pva::ConfigurationBuilder()
141  .add("EPICS_PVA_ADDR_LIST", conf->getSubFieldT<pvd::PVString>("addrlist")->get())
142  .add("EPICS_PVA_AUTO_ADDR_LIST", conf->getSubFieldT<pvd::PVScalar>("autoaddrlist")->getAs<std::string>())
143  .add("EPICS_PVA_SERVER_PORT", conf->getSubFieldT<pvd::PVScalar>("serverport")->getAs<pvd::uint16>())
144  .add("EPICS_PVA_BROADCAST_PORT", conf->getSubFieldT<pvd::PVScalar>("bcastport")->getAs<pvd::uint16>())
145  .add("EPICS_PVA_DEBUG", arg.debug>=5 ? 5 : 0)
146  .push_map()
147  .build());
148 
149  pva::ChannelProvider::shared_pointer base(pva::ChannelProviderRegistry::clients()->createProvider(provider, C));
150  if(!base)
151  throw std::runtime_error("Can't create ChannelProvider");
152 
153  GWServerChannelProvider::shared_pointer ret(new GWServerChannelProvider(base));
154  return ret;
155 }
156 
157 pva::ServerContext::shared_pointer configure_server(ServerConfig& arg, const pvd::PVStructurePtr& conf)
158 {
159  std::string name(conf->getSubFieldT<pvd::PVString>("name")->get());
160 
161  LOG(pva::logLevelInfo, "Configure server '%s'", name.c_str());
162 
163  pva::Configuration::shared_pointer C(pva::ConfigurationBuilder()
164  .add("EPICS_PVAS_INTF_ADDR_LIST", conf->getSubFieldT<pvd::PVString>("interface")->get())
165  .add("EPICS_PVAS_BEACON_ADDR_LIST", conf->getSubFieldT<pvd::PVString>("addrlist")->get())
166  .add("EPICS_PVAS_AUTO_BEACON_ADDR_LIST", conf->getSubFieldT<pvd::PVScalar>("autoaddrlist")->getAs<std::string>())
167  .add("EPICS_PVAS_SERVER_PORT", conf->getSubFieldT<pvd::PVScalar>("serverport")->getAs<pvd::uint16>())
168  .add("EPICS_PVAS_BROADCAST_PORT", conf->getSubFieldT<pvd::PVScalar>("bcastport")->getAs<pvd::uint16>())
169  .add("EPICS_PVA_DEBUG", arg.debug>=5 ? 5 : 0)
170  .push_map()
171  .build());
172 
173  pvd::PVStringArray::shared_pointer clients(conf->getSubFieldT<pvd::PVStringArray>("clients"));
174  pvd::PVStringArray::const_svector names(clients->view());
175  std::vector<pva::ChannelProvider::shared_pointer> providers;
176 
177  for(pvd::PVStringArray::const_svector::const_iterator it(names.begin()), end(names.end()); it!=end; ++it)
178  {
179  ServerConfig::clients_t::const_iterator it2(arg.clients.find(*it));
180  if(it2==arg.clients.end())
181  throw std::runtime_error("Server references non-existant client");
182  providers.push_back(it2->second);
183  }
184 
185  pva::ServerContext::shared_pointer ret(pva::ServerContext::create(pva::ServerContext::Config()
186  .config(C)
187  .providers(providers)));
188  return ret;
189 }
190 
191 volatile int quit;
192 epicsEvent done;
193 
194 #ifdef USE_SIGNAL
195 void sigdone(int num)
196 {
197  (void)num;
198  quit = 1;
199  done.signal();
200 }
201 #endif
202 
203 ServerConfig* volatile theserver;
204 
205 void iocsh_drop(const char *client, const char *channel)
206 {
207  if(!theserver)
208  return;
209  try {
210  theserver->drop(client, channel);
211  }catch(std::exception& e){
212  std::cout<<"Error: "<<e.what()<<"\n";
213  }
214 }
215 
216 void gwsr(int lvl, const char *server)
217 {
218  if(!theserver)
219  return;
220  try {
221  theserver->status_server(lvl, server);
222  }catch(std::exception& e){
223  std::cout<<"Error: "<<e.what()<<"\n";
224  }
225 }
226 
227 void gwcr(int lvl, const char *client, const char *channel)
228 {
229  if(!theserver)
230  return;
231  try {
232  theserver->status_client(lvl, client, channel);
233  }catch(std::exception& e){
234  std::cout<<"Error: "<<e.what()<<"\n";
235  }
236 }
237 
238 }// namespace
239 
240 int main(int argc, char *argv[])
241 {
242  try {
243  pva::refTrackRegistrar();
244 
245  epics::iocshRegister<const char*, const char*, &iocsh_drop>("drop", "client", "channel");
246  epics::iocshRegister<int, const char*, &gwsr>("gwsr", "level", "channel");
247  epics::iocshRegister<int, const char*, const char*, &gwcr>("gwcr", "level", "client", "channel");
248 
249  libComRegister();
250  registerReadOnly();
251  epics::registerRefCounter("ChannelCacheEntry", &ChannelCacheEntry::num_instances);
252  epics::registerRefCounter("ChannelCacheEntry::CRequester", &ChannelCacheEntry::CRequester::num_instances);
253  epics::registerRefCounter("GWChannel", &GWChannel::num_instances);
254  epics::registerRefCounter("MonitorCacheEntry", &MonitorCacheEntry::num_instances);
255  epics::registerRefCounter("MonitorUser", &MonitorUser::num_instances);
256 
257  ServerConfig arg;
258  theserver = &arg;
259  getargs(arg, argc, argv);
260 
261  if(arg.debug>0)
262  std::cout<<"Notice: This p2p gateway prototype has been superceded by the p4p.gw gateway\n"
263  " which has exciting new features including granular access control,\n"
264  " and status PVs including bandwidth usage reports.\n"
265  " p2p is considered deprecated by its author, and will receive\n"
266  " minimal maintainance effort going forward.\n"
267  " Users are encouraged to migrate to p4p.gw.\n"
268  "\n"
269  " https://mdavidsaver.github.io/p4p/gw.html\n"
270  "\n";
271 
272  pva::pvAccessLogLevel lvl;
273  if(arg.debug<0)
274  lvl = pva::logLevelError;
275  else if(arg.debug==0)
276  lvl = pva::logLevelWarn;
277  else if(arg.debug==1)
278  lvl = pva::logLevelInfo;
279  else if(arg.debug==2)
280  lvl = pva::logLevelDebug;
281  else if(arg.debug==3)
282  lvl = pva::logLevelTrace;
283  else if(arg.debug>=4)
284  lvl = pva::logLevelAll;
285  SET_LOG_LEVEL(lvl);
286 
287  pva::ClientFactory::start();
288 
289  pvd::PVStructureArray::const_svector arr;
290 
291  arr = arg.conf->getSubFieldT<pvd::PVStructureArray>("clients")->view();
292 
293  for(size_t i=0; i<arr.size(); i++) {
294  if(!arr[i]) continue;
295  const pvd::PVStructurePtr& client = arr[i];
296 
297  std::string name(client->getSubFieldT<pvd::PVString>("name")->get());
298  if(name.empty())
299  throw std::runtime_error("Client with empty name not allowed");
300 
301  ServerConfig::clients_t::const_iterator it(arg.clients.find(name));
302  if(it!=arg.clients.end())
303  throw std::runtime_error(std::string("Duplicate client name not allowed : ")+name);
304 
305  arg.clients[name] = configure_client(arg, client);
306  }
307 
308  arr = arg.conf->getSubFieldT<pvd::PVStructureArray>("servers")->view();
309 
310  for(size_t i=0; i<arr.size(); i++) {
311  if(!arr[i]) continue;
312  const pvd::PVStructurePtr& server = arr[i];
313 
314  std::string name(server->getSubFieldT<pvd::PVString>("name")->get());
315  if(name.empty())
316  throw std::runtime_error("Server with empty name not allowed");
317 
318  ServerConfig::servers_t::const_iterator it(arg.servers.find(name));
319  if(it!=arg.servers.end())
320  throw std::runtime_error(std::string("Duplicate server name not allowed : ")+name);
321 
322  arg.servers[name] = configure_server(arg, server);
323  }
324 
325  int ret = 0;
326  if(arg.interactive) {
327  ret = iocsh(NULL);
328  } else {
329 #ifdef USE_SIGNAL
330  signal(SIGINT, sigdone);
331  signal(SIGTERM, sigdone);
332  signal(SIGQUIT, sigdone);
333 #endif
334 
335  while(!quit) {
336  done.wait();
337  }
338  }
339 
340  theserver = 0;
341 
342  return ret;
343  }catch(std::exception& e){
344  std::cerr<<"Fatal Error : "<<e.what()<<"\n";
345  return 1;
346  }
347 }