OBECA Receive Process
RestHandler.cpp
1 // OBECA - Open Broadcast Edge Cache Appliance
2 // Receive 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, LimeSdrReader& lime, Phy& phy,
38  : _cfg(cfg),
39  _state(state),
40  _lime(lime),
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/obeca/cert.pem";
49  cfg.lookupValue("restful_api.cert", cert_file);
50 
51  std::string key_file = "/usr/share/obeca/key.pem";
52  cfg.lookupValue("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("restful_api.api_key.enabled", _require_bearer_token);
61  if (_require_bearer_token) {
62  _api_key = "106cd60-76c8-4c37-944c-df21aa690c1e";
63  cfg.lookupValue("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()));
80  if (_require_bearer_token &&
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(_lime.get_frequency());
117  sdr["gain"] = value(_lime.get_gain());
118  sdr["filter_bw"] = value(_lime.get_filter_bw());
119  sdr["antenna"] = value(_lime.get_antenna());
120  sdr["sample_rate"] = value(_lime.get_sample_rate());
121  sdr["buffer_level"] = value(_lime.get_buffer_level());
122  message.reply(status_codes::OK, sdr);
123  } else if (paths[0] == "ce_values") {
124  auto cestream = Concurrency::streams::bytestream::open_istream(_ce_values);
125  message.reply(status_codes::OK, cestream);
126  } else if (paths[0] == "pdsch_status") {
127  value sdr = value::object();
128  sdr["bler"] = value(static_cast<float>(_pdsch.errors) /
129  static_cast<float>(_pdsch.total));
130  sdr["ber"] = value(_pdsch.ber);
131  sdr["mcs"] = value(_pdsch.mcs);
132  sdr["present"] = 1;
133  message.reply(status_codes::OK, sdr);
134  } else if (paths[0] == "pdsch_data") {
135  auto cestream = Concurrency::streams::bytestream::open_istream(_pdsch.GetData());
136  message.reply(status_codes::OK, cestream);
137  } else if (paths[0] == "mcch_status") {
138  value sdr = value::object();
139  sdr["bler"] = value(static_cast<float>(_mcch.errors) /
140  static_cast<float>(_mcch.total));
141  sdr["ber"] = value(_mcch.ber);
142  sdr["mcs"] = value(_mcch.mcs);
143  sdr["present"] = 1;
144  message.reply(status_codes::OK, sdr);
145  } else if (paths[0] == "mcch_data") {
146  auto cestream = Concurrency::streams::bytestream::open_istream(_mcch.GetData());
147  message.reply(status_codes::OK, cestream);
148  } else if (paths[0] == "mch_info") {
149  std::vector<value> mi;
150  auto mch_info = _phy.mch_info();
151  std::for_each(std::begin(mch_info), std::end(mch_info), [&mi](Phy::mch_info_t const& mch) {
152  value m;
153  m["mcs"] = value(mch.mcs);
154  std::vector<value> mti;
155  std::for_each(std::begin(mch.mtchs), std::end(mch.mtchs), [&mti](Phy::mtch_info_t const& mtch) {
156  value mt;
157  mt["tmgi"] = value(mtch.tmgi);
158  mt["dest"] = value(mtch.dest);
159  mt["lcid"] = value(mtch.lcid);
160  mti.push_back(mt);
161  });
162  m["mtchs"] = value::array(mti);
163  mi.push_back(m);
164  });
165  message.reply(status_codes::OK, value::array(mi));
166  } else if (paths[0] == "mch_status") {
167  int idx = std::stoi(paths[1]);
168  value sdr = value::object();
169  sdr["bler"] = value(static_cast<float>(_mch[idx].errors) /
170  static_cast<float>(_mch[idx].total));
171  sdr["ber"] = value(_mch[idx].ber);
172  sdr["mcs"] = value(_mch[idx].mcs);
173  sdr["present"] = value(_mch[idx].present);
174  message.reply(status_codes::OK, sdr);
175  } else if (paths[0] == "mch_data") {
176  int idx = std::stoi(paths[1]);
177  auto cestream = Concurrency::streams::bytestream::open_istream(_mch[idx].GetData());
178  message.reply(status_codes::OK, cestream);
179  } else if (paths[0] == "log") {
180  std::string logfile = "/var/log/syslog";
181 
182  Concurrency::streams::file_stream<uint8_t>::open_istream(logfile).then(
183  [message](const Concurrency::streams::basic_istream<unsigned char>&
184  file_stream) {
185  message.reply(status_codes::OK, file_stream, "text/plain");
186  });
187  }
188  }
189 }
190 
191 void RestHandler::put(http_request message) {
192  spdlog::debug("Received PUT request {}", message.to_string() );
193 
194  if (_require_bearer_token &&
195  (message.headers()["Authorization"] != "Bearer " + _api_key)) {
196  message.reply(status_codes::Unauthorized);
197  return;
198  }
199 
200  auto paths = uri::split_path(uri::decode(message.relative_uri().path()));
201  if (paths.empty()) {
202  message.reply(status_codes::NotFound);
203  } else {
204  if (paths[0] == "sdr_params") {
205  value answer;
206 
207  const auto & jval = message.extract_json().get();
208 
209  _set_params(
210  jval.at("antenna").as_string(),
211  jval.at("frequency").as_integer(),
212  jval.at("gain").as_double(),
213  jval.at("sample_rate").as_integer(),
214  jval.at("filter_bw").as_integer());
215 
216  message.reply(status_codes::OK, answer);
217  }
218  }
219 }
220 
221 void RestHandler::add_cinr_value( float cinr) {
222  if (_cinr_db.size() > CINR_RAVG_CNT) {
223  _cinr_db.erase(_cinr_db.begin());
224  }
225  _cinr_db.push_back(cinr);
226 }
LimeSdrReader::get_buffer_level
double get_buffer_level()
Get current ringbuffer level (0 = empty .
Definition: LimeSdrReader.h:117
LimeSdrReader::get_frequency
double get_frequency()
Get current center frequency.
Definition: LimeSdrReader.h:102
LimeSdrReader::get_antenna
std::string get_antenna()
Get current antenna port.
Definition: LimeSdrReader.h:122
RestHandler::_pdsch
ChannelInfo _pdsch
RX info for PDSCH.
Definition: RestHandler.h:101
Phy
The PHY component.
Definition: Phy.h:43
Phy::mtch_info_t
Definition: Phy.h:158
LimeSdrReader::get_sample_rate
double get_sample_rate()
Get current sample rate.
Definition: LimeSdrReader.h:97
Phy::cfo
float cfo()
Get the current CFO value.
Definition: Phy.h:109
RestHandler::~RestHandler
virtual ~RestHandler()
Default destructor.
set_params
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:191
LimeSdrReader::get_filter_bw
unsigned get_filter_bw()
Get current filter bandwidth.
Definition: LimeSdrReader.h:107
RestHandler::set_params_t
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:52
RestHandler::_mch
std::map< uint32_t, ChannelInfo > _mch
RX info for MCHs.
Definition: RestHandler.h:111
RestHandler::_ce_values
std::vector< uint8_t > _ce_values
Time domain subcarrier CE values.
Definition: RestHandler.h:96
Phy::cell
srslte_cell_t cell()
Get the current cell (with params adjusted for MBSFN)
Definition: Phy.h:92
RestHandler::_mcch
ChannelInfo _mcch
RX info for MCCH.
Definition: RestHandler.h:106
RestHandler::RestHandler
RestHandler(const libconfig::Config &cfg, const std::string &url, state_t &state, LimeSdrReader &lime, Phy &phy, set_params_t set_params)
Default constructor.
Definition: RestHandler.cpp:35
LimeSdrReader::get_gain
double get_gain()
Get current gain.
Definition: LimeSdrReader.h:112
RestHandler::cinr_db
float cinr_db()
Current CINR value.
Definition: RestHandler.h:116
Phy::mch_info_t
Definition: Phy.h:163
LimeSdrReader
Interface to LimeSDR via LimeSuite.
Definition: LimeSdrReader.h:36