5G-MAG Reference Tools - MBMS Modem
RestHandler.cpp
Go to the documentation of this file.
1 // 5G-MAG Reference Tools
2 // MBMS Modem Process
3 //
4 // Copyright (C) 2021 Klaus Kühnhammer (Österreichische Rundfunksender GmbH & Co KG)
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU Affero General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU Affero General Public License for more details.
15 //
16 // You should have received a copy of the GNU Affero General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 //
19 
20 #include "RestHandler.h"
21 
22 #include <memory>
23 #include <utility>
24 
25 #include "spdlog/spdlog.h"
26 
27 using web::json::value;
28 using web::http::methods;
29 using web::http::uri;
30 using web::http::http_request;
31 using web::http::status_codes;
32 using web::http::experimental::listener::http_listener;
33 using web::http::experimental::listener::http_listener_config;
34 
35 RestHandler::RestHandler(const libconfig::Config& cfg, const std::string& url,
36  state_t& state, SdrReader& sdr, Phy& phy,
38  : _cfg(cfg),
39  _state(state),
40  _sdr(sdr),
41  _phy(phy),
42  _set_params(std::move(set_params)) {
43 
44  http_listener_config server_config;
45  if (url.rfind("https", 0) == 0) {
46  server_config.set_ssl_context_callback(
47  [&](boost::asio::ssl::context& ctx) {
48  std::string cert_file = "/usr/share/5gmag-rt/cert.pem";
49  cfg.lookupValue("modem.restful_api.cert", cert_file);
50 
51  std::string key_file = "/usr/share/5gmag-rt/key.pem";
52  cfg.lookupValue("modem.restful_api.key", key_file);
53 
54  ctx.set_options(boost::asio::ssl::context::default_workarounds);
55  ctx.use_certificate_chain_file(cert_file);
56  ctx.use_private_key_file(key_file, boost::asio::ssl::context::pem);
57  });
58  }
59 
60  cfg.lookupValue("modem.restful_api.api_key.enabled", _require_bearer_token);
62  _api_key = "106cd60-76c8-4c37-944c-df21aa690c1e";
63  cfg.lookupValue("modem.restful_api.api_key.key", _api_key);
64  }
65 
66  _listener = std::make_unique<http_listener>(
67  url, server_config);
68 
69  _listener->support(methods::GET, std::bind(&RestHandler::get, this, std::placeholders::_1)); // NOLINT
70  _listener->support(methods::PUT, std::bind(&RestHandler::put, this, std::placeholders::_1)); // NOLINT
71 
72  _listener->open().wait();
73 }
74 
75 RestHandler::~RestHandler() = default;
76 
77 void RestHandler::get(http_request message) {
78  spdlog::debug("Received GET request {}", message.to_string() );
79  auto paths = uri::split_path(uri::decode(message.relative_uri().path()));
81  (message.headers()["Authorization"] != "Bearer " + _api_key)) {
82  message.reply(status_codes::Unauthorized);
83  return;
84  }
85 
86  if (paths.empty()) {
87  message.reply(status_codes::NotFound);
88  } else {
89  if (paths[0] == "status") {
90  auto state = value::object();
91 
92  switch (_state) {
93  case searching:
94  state["state"] = value::string("searching");
95  break;
96  case syncing:
97  state["state"] = value::string("syncing");
98  break;
99  case processing:
100  state["state"] = value::string("synchronized");
101  break;
102  }
103 
104  if (_phy.cell().nof_prb == _phy.cell().mbsfn_prb) {
105  state["nof_prb"] = value(_phy.cell().nof_prb);
106  } else {
107  state["nof_prb"] = value(_phy.cell().mbsfn_prb);
108  }
109  state["cell_id"] = value(_phy.cell().id);
110  state["cfo"] = value(_phy.cfo());
111  state["cinr_db"] = value(cinr_db());
112  state["subcarrier_spacing"] = value(_phy.mbsfn_subcarrier_spacing_khz());
113  message.reply(status_codes::OK, state);
114  } else if (paths[0] == "sdr_params") {
115  value sdr = value::object();
116  sdr["frequency"] = value(_sdr.get_frequency());
117  sdr["gain"] = value(_sdr.get_gain());
118  sdr["min_gain"] = value(_sdr.min_gain());
119  sdr["max_gain"] = value(_sdr.max_gain());
120  sdr["filter_bw"] = value(_sdr.get_filter_bw());
121  sdr["antenna"] = value(_sdr.get_antenna());
122  sdr["sample_rate"] = value(_sdr.get_sample_rate());
123  sdr["buffer_level"] = value(_sdr.get_buffer_level());
124  message.reply(status_codes::OK, sdr);
125  } else if (paths[0] == "ce_values") {
126  auto cestream = Concurrency::streams::bytestream::open_istream(_ce_values);
127  message.reply(status_codes::OK, cestream);
128  } else if (paths[0] == "pdsch_status") {
129  value sdr = value::object();
130  sdr["bler"] = value(static_cast<float>(_pdsch.errors) /
131  static_cast<float>(_pdsch.total));
132  sdr["ber"] = value(_pdsch.ber);
133  sdr["mcs"] = value(_pdsch.mcs);
134  sdr["present"] = 1;
135  message.reply(status_codes::OK, sdr);
136  } else if (paths[0] == "pdsch_data") {
137  auto cestream = Concurrency::streams::bytestream::open_istream(_pdsch.GetData());
138  message.reply(status_codes::OK, cestream);
139  } else if (paths[0] == "mcch_status") {
140  value sdr = value::object();
141  sdr["bler"] = value(static_cast<float>(_mcch.errors) /
142  static_cast<float>(_mcch.total));
143  sdr["ber"] = value(_mcch.ber);
144  sdr["mcs"] = value(_mcch.mcs);
145  sdr["present"] = 1;
146  message.reply(status_codes::OK, sdr);
147  } else if (paths[0] == "mcch_data") {
148  auto cestream = Concurrency::streams::bytestream::open_istream(_mcch.GetData());
149  message.reply(status_codes::OK, cestream);
150  } else if (paths[0] == "mch_info") {
151  std::vector<value> mi;
152  auto mch_info = _phy.mch_info();
153  std::for_each(std::begin(mch_info), std::end(mch_info), [&mi](Phy::mch_info_t const& mch) {
154  value m;
155  m["mcs"] = value(mch.mcs);
156  std::vector<value> mti;
157  std::for_each(std::begin(mch.mtchs), std::end(mch.mtchs), [&mti](Phy::mtch_info_t const& mtch) {
158  value mt;
159  mt["tmgi"] = value(mtch.tmgi);
160  mt["dest"] = value(mtch.dest);
161  mt["lcid"] = value(mtch.lcid);
162  mti.push_back(mt);
163  });
164  m["mtchs"] = value::array(mti);
165  mi.push_back(m);
166  });
167  message.reply(status_codes::OK, value::array(mi));
168  } else if (paths[0] == "mch_status") {
169  int idx = std::stoi(paths[1]);
170  value sdr = value::object();
171  sdr["bler"] = value(static_cast<float>(_mch[idx].errors) /
172  static_cast<float>(_mch[idx].total));
173  sdr["ber"] = value(_mch[idx].ber);
174  sdr["mcs"] = value(_mch[idx].mcs);
175  sdr["present"] = value(_mch[idx].present);
176  message.reply(status_codes::OK, sdr);
177  } else if (paths[0] == "mch_data") {
178  int idx = std::stoi(paths[1]);
179  auto cestream = Concurrency::streams::bytestream::open_istream(_mch[idx].GetData());
180  message.reply(status_codes::OK, cestream);
181  } else if (paths[0] == "log") {
182  std::string logfile = "/var/log/syslog";
183 
184  Concurrency::streams::file_stream<uint8_t>::open_istream(logfile).then(
185  [message](const Concurrency::streams::basic_istream<unsigned char>&
186  file_stream) {
187  message.reply(status_codes::OK, file_stream, "text/plain");
188  });
189  }
190  }
191 }
192 
193 void RestHandler::put(http_request message) {
194  spdlog::debug("Received PUT request {}", message.to_string() );
195 
196  if (_require_bearer_token &&
197  (message.headers()["Authorization"] != "Bearer " + _api_key)) {
198  message.reply(status_codes::Unauthorized);
199  return;
200  }
201 
202  auto paths = uri::split_path(uri::decode(message.relative_uri().path()));
203  if (paths.empty()) {
204  message.reply(status_codes::NotFound);
205  } else {
206  if (paths[0] == "sdr_params") {
207  value answer;
208 
209  auto f = _sdr.get_frequency();
210  auto g = _sdr.get_gain();
211  auto bw = _sdr.get_filter_bw();
212  auto a = _sdr.get_antenna();
213  auto sr = _sdr.get_sample_rate();
214 
215  const auto & jval = message.extract_json().get();
216  spdlog::debug("Received JSON: {}", jval.serialize());
217 
218  if (jval.has_field("antenna")) {
219  a = jval.at("antenna").as_string();
220  }
221  if (jval.has_field("frequency")) {
222  f = jval.at("frequency").as_integer();
223  }
224  if (jval.has_field("gain")) {
225  g = jval.at("gain").as_double();
226  }
227  _set_params( a, f, g, sr, bw);
228 
229  message.reply(status_codes::OK, answer);
230  }
231  }
232 }
233 
234 void RestHandler::add_cinr_value( float cinr) {
235  if (_cinr_db.size() > CINR_RAVG_CNT) {
236  _cinr_db.erase(_cinr_db.begin());
237  }
238  _cinr_db.push_back(cinr);
239 }
state_t
Definition: RestHandler.h:39
@ syncing
Definition: RestHandler.h:39
@ searching
Definition: RestHandler.h:39
@ processing
Definition: RestHandler.h:39
const int CINR_RAVG_CNT
Definition: RestHandler.h:38
The PHY component.
Definition: Phy.h:42
const std::vector< mch_info_t > & mch_info()
Definition: Phy.h:175
srsran_cell_t cell()
Get the current cell (with params adjusted for MBSFN)
Definition: Phy.h:91
float mbsfn_subcarrier_spacing_khz()
Definition: Phy.h:197
float cfo()
Get the current CFO value.
Definition: Phy.h:108
std::vector< uint8_t > GetData()
Definition: RestHandler.h:76
SdrReader & _sdr
Definition: RestHandler.h:126
std::unique_ptr< web::http::experimental::listener::http_listener > _listener
Definition: RestHandler.h:123
float cinr_db()
Current CINR value.
Definition: RestHandler.h:113
std::vector< uint8_t > _ce_values
Time domain subcarrier CE values.
Definition: RestHandler.h:93
void put(web::http::http_request message)
ChannelInfo _mcch
RX info for MCCH.
Definition: RestHandler.h:103
std::string _api_key
Definition: RestHandler.h:132
std::vector< float > _cinr_db
Definition: RestHandler.h:117
void add_cinr_value(float cinr)
std::map< uint32_t, ChannelInfo > _mch
RX info for MCHs.
Definition: RestHandler.h:108
std::function< void(const std::string &antenna, unsigned fcen, double gain, unsigned sample_rate, unsigned bandwidth)> set_params_t
Definition of the callback for setting new reception parameters.
Definition: RestHandler.h:49
RestHandler(const libconfig::Config &cfg, const std::string &url, state_t &state, SdrReader &sdr, Phy &phy, set_params_t set_params)
Default constructor.
Definition: RestHandler.cpp:35
void get(web::http::http_request message)
Definition: RestHandler.cpp:77
ChannelInfo _pdsch
RX info for PDSCH.
Definition: RestHandler.h:98
virtual ~RestHandler()
Default destructor.
state_t & _state
Definition: RestHandler.h:125
bool _require_bearer_token
Definition: RestHandler.h:131
set_params_t _set_params
Definition: RestHandler.h:129
Interface to the SDR stick.
Definition: SdrReader.h:37
double get_frequency()
Get current center frequency.
Definition: SdrReader.h:100
double get_buffer_level()
Get current ringbuffer level (0 = empty .
Definition: SdrReader.cpp:369
unsigned get_filter_bw()
Get current filter bandwidth.
Definition: SdrReader.h:105
std::string get_antenna()
Get current antenna port.
Definition: SdrReader.h:120
double max_gain()
Definition: SdrReader.h:129
double min_gain()
Definition: SdrReader.h:127
double get_gain()
Get current gain.
Definition: SdrReader.h:110
double get_sample_rate()
Get current sample rate.
Definition: SdrReader.h:95
static Config cfg
Global configuration object.
Definition: main.cpp:165
void set_params(const std::string &ant, unsigned fc, double g, unsigned sr, unsigned bw)
Set new SDR parameters and initialize resynchronisation.
Definition: main.cpp:198
std::vector< mtch_info_t > mtchs
Definition: Phy.h:172