ttcn3: make SS single threaded and use SCTP for test ports

- remove all threading from SS
- use epoll to handle test port connections, signals, and timers
- convert testport interface to SCTP
This commit is contained in:
Andre Puschmann 2020-02-21 14:42:44 +01:00
parent 6d88b640b7
commit b88e877b4c
15 changed files with 1008 additions and 686 deletions

View File

@ -0,0 +1,173 @@
/*
* Copyright 2013-2019 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
/*! \brief Common helper function for epoll
*
*/
#ifndef SRSLTE_EPOLL_HELPER_H
#define SRSLTE_EPOLL_HELPER_H
#include <functional>
#include <signal.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include <sys/timerfd.h>
#include <unistd.h>
///< A virtual interface to handle epoll events (used by timer and port handler)
class epoll_handler
{
public:
virtual int handle_event(int fd, epoll_event e, int epoll_fd) = 0;
};
///< Callback function called when timer expires
using epoll_timer_callback = std::function<void(uint64_t res)>;
///< Epoll timer handler
class epoll_timer_handler : public epoll_handler
{
public:
epoll_timer_handler(int fd_, epoll_timer_callback callback_) : timer_fd(fd_), callback(callback_){};
int handle_event(int fd, epoll_event e, int epoll_fd)
{
uint64_t res;
int ret = read(fd, &res, sizeof(res));
callback(res);
return ret;
}
int get_timer_fd() { return timer_fd; };
private:
int timer_fd = -1;
epoll_timer_callback callback;
};
///< Basic epoll signal handler
class epoll_signal_handler : public epoll_handler
{
public:
epoll_signal_handler(bool* running_) : running(running_) {}
int handle_event(int fd, epoll_event e, int epoll_fd)
{
struct signalfd_siginfo info;
if (read(fd, &info, sizeof(info)) != sizeof(info)) {
fprintf(stderr, "failed to read signal fd buffer\n");
return SRSLTE_ERROR;
}
switch (info.ssi_signo) {
case SIGTERM:
case SIGINT:
case SIGHUP:
case SIGQUIT:
*running = false;
break;
default:
fprintf(stderr, "got signal %d\n", info.ssi_signo);
break;
}
return SRSLTE_SUCCESS;
}
private:
bool* running = nullptr;
};
///< Create periodic epoll timer every 1ms
int create_tti_timer()
{
int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
if (timer_fd == -1) {
printf("timerfd_create() failed: errno=%d\n", errno);
return SRSLTE_ERROR;
}
int msec = 1; // our 1ms TTI timer
struct itimerspec ts = {};
ts.it_value.tv_sec = msec / 1000;
ts.it_value.tv_nsec = (msec % 1000) * 1000000;
ts.it_interval.tv_sec = msec / 1000;
ts.it_interval.tv_nsec = (msec % 1000) * 1000000;
if (timerfd_settime(timer_fd, 0, &ts, NULL) < 0) {
printf("timerfd_settime() failed: errno=%d\n", errno);
close(timer_fd);
return SRSLTE_ERROR;
}
return timer_fd;
}
///< Create signalfd for handling signals
int add_signalfd()
{
// block all signals. we take signals synchronously via signalfd
sigset_t all;
sigfillset(&all);
sigprocmask(SIG_SETMASK, &all, NULL);
// add signals we accept synchronously via signalfd
std::vector<int> sigs = {SIGIO, SIGHUP, SIGTERM, SIGINT, SIGQUIT, SIGALRM};
sigset_t sw;
sigemptyset(&sw);
for (auto& sig : sigs) {
sigaddset(&sw, sig);
}
// create the signalfd for receiving signals
int sig_fd = signalfd(-1, &sw, 0);
if (sig_fd == -1) {
fprintf(stderr, "signalfd: %s\n", strerror(errno));
return SRSLTE_ERROR;
}
return sig_fd;
}
///< Add fd to epoll fd
int add_epoll(int fd, int epoll_fd)
{
struct epoll_event ev = {};
ev.data.fd = fd;
ev.events = EPOLLIN;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
fprintf(stderr, "epoll_ctl failed for fd=%d\n", fd);
return SRSLTE_ERROR;
}
return SRSLTE_SUCCESS;
}
///< Remove fd from epoll
int del_epoll(int fd, int epoll_fd)
{
struct epoll_event ev = {};
ev.data.fd = fd;
ev.events = EPOLLIN;
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev) == -1) {
fprintf(stderr, "epoll_ctl failed for fd=%d\n", fd);
return SRSLTE_ERROR;
}
return SRSLTE_SUCCESS;
}
#endif // SRSLTE_EPOLL_HELPER_H

View File

@ -45,7 +45,7 @@ public:
typedef std::vector<cell_t> cell_list_t; typedef std::vector<cell_t> cell_list_t;
lte_ttcn3_phy(srslte::logger* logger_); lte_ttcn3_phy(srslte::logger* logger_);
~lte_ttcn3_phy(); ~lte_ttcn3_phy() = default;
int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, syssim_interface_phy* syssim_); int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, syssim_interface_phy* syssim_);

View File

@ -0,0 +1,35 @@
/*
* Copyright 2013-2019 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
/*! \brief Common types for TTCN3 test systems
*
*/
#ifndef SRSUE_TTCN3_COMMON_H
#define SRSUE_TTCN3_COMMON_H
#include <memory>
const static uint32_t RX_BUF_SIZE = 1024 * 1024;
typedef std::array<uint8_t, RX_BUF_SIZE> byte_array_t;
typedef std::unique_ptr<byte_array_t> unique_byte_array_t;
#endif // SRSUE_TTCN3_COMMON_H

View File

@ -24,78 +24,56 @@
#include "rapidjson/document.h" #include "rapidjson/document.h"
#include "rapidjson/prettywriter.h" #include "rapidjson/prettywriter.h"
#include "srslte/common/netsource_handler.h"
#include "srslte/phy/io/netsource.h" #include "srslte/phy/io/netsource.h"
#include "ttcn3_helpers.h" #include "ttcn3_helpers.h"
#include "ttcn3_port_handler.h"
using namespace rapidjson; using namespace rapidjson;
// The IP CTRL interface to the IP_PTC // The IP CTRL interface to the IP_PTC
class ttcn3_ip_ctrl_interface : public netsource_handler class ttcn3_ip_ctrl_interface : public ttcn3_port_handler
{ {
public: public:
ttcn3_ip_ctrl_interface() : netsource_handler("TTCN3_IP_CTRL_IF"){}; ttcn3_ip_ctrl_interface() = default;
~ttcn3_ip_ctrl_interface(){}; ~ttcn3_ip_ctrl_interface() = default;
void init(srslte::log* log_, std::string net_ip_, uint32_t net_port_) int init(srslte::log* log_, std::string net_ip_, uint32_t net_port_)
{ {
net_ip = net_ip_; net_ip = net_ip_;
net_port = net_port_; net_port = net_port_;
log = log_; log = log_;
initialized = true; initialized = true;
log->debug("Initialized.\n"); log->debug("Initialized.\n");
return port_listen();
} }
private: private:
void run_thread() ///< Main message handler
int handle_message(const unique_byte_array_t& rx_buf, const uint32_t n)
{ {
// open TCP socket log->debug("Received %d B from remote.\n", n);
if (srslte_netsource_init(&net_source, net_ip.c_str(), net_port, SRSLTE_NETSOURCE_TCP)) {
fprintf(stderr, "Error creating input TCP socket at port %d\n", net_port); Document document;
exit(-1); if (document.Parse((char*)rx_buf->begin()).HasParseError() || document.IsObject() == false) {
log->error_hex(rx_buf->begin(), n, "Error parsing incoming data.\n");
return SRSLTE_ERROR;
} }
log->info("Listening on %s:%d for incoming connections ..\n", net_ip.c_str(), net_port); // Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info("Received %d bytes\n%s\n", n, (char*)buffer.GetString());
running = true; // Get message
if (document.HasMember("RoutingInfo")) {
int n; log->info("Received RoutingInfo\n");
while (run_enable) { handle_routing_info(document);
log->debug("Reading from IP_CTRL port ..\n"); } else {
n = srslte_netsource_read(&net_source, rx_buf->begin(), RX_BUF_SIZE); log->error("Received unknown request.\n");
if (n > 0) {
rx_buf->at(n) = '\0';
Document document;
if (document.Parse((char*)rx_buf->begin()).HasParseError()) {
log->error_hex(rx_buf->begin(), n, "Error parsing incoming data.\n");
break;
}
assert(document.IsObject());
// Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info("Received %d bytes\n%s\n", n, (char*)buffer.GetString());
// Get message
if (document.HasMember("RoutingInfo")) {
log->info("Received RoutingInfo\n");
handle_routing_info(document);
} else {
log->error("Received unknown request.\n");
}
} else if (n == 0) {
log->error("Receiving null from network\n");
} else {
log->error("Error receiving from network\n");
}
} }
running = false; return SRSLTE_SUCCESS;
srslte_netsource_free(&net_source);
} }
void handle_routing_info(Document& document) void handle_routing_info(Document& document)
@ -118,9 +96,7 @@ private:
std::string resp = ttcn3_helpers::get_drbmux_common_ind_cnf(); std::string resp = ttcn3_helpers::get_drbmux_common_ind_cnf();
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length()); log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
if (srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length()) != SRSLTE_SUCCESS) { send((const uint8_t*)resp.c_str(), resp.length());
log->error("Error sending message to tester.\n");
};
} }
}; };

View File

@ -22,78 +22,52 @@
#ifndef SRSUE_TTCN3_IP_SOCK_INTERFACE_H #ifndef SRSUE_TTCN3_IP_SOCK_INTERFACE_H
#define SRSUE_TTCN3_IP_SOCK_INTERFACE_H #define SRSUE_TTCN3_IP_SOCK_INTERFACE_H
#include "ttcn3_port_handler.h"
// The IP Socket interface to the IP_PTC // The IP Socket interface to the IP_PTC
class ttcn3_ip_sock_interface : public netsource_handler class ttcn3_ip_sock_interface : public ttcn3_port_handler
{ {
public: public:
ttcn3_ip_sock_interface() : netsource_handler("TTCN3_IP_SOCK_IF"){}; ttcn3_ip_sock_interface() = default;
~ttcn3_ip_sock_interface(){}; ~ttcn3_ip_sock_interface() = default;
void init(srslte::log* log_, std::string net_ip_, uint32_t net_port_) int init(srslte::log* log_, std::string net_ip_, uint32_t net_port_)
{ {
net_ip = net_ip_; net_ip = net_ip_;
net_port = net_port_; net_port = net_port_;
log = log_; log = log_;
initialized = true; initialized = true;
log->debug("Initialized.\n"); log->debug("Initialized.\n");
return port_listen();
} }
private: private:
void run_thread() ///< Main message handler
int handle_message(const unique_byte_array_t& rx_buf, const uint32_t n)
{ {
if (!initialized) { log->debug("Received %d B from remote.\n", n);
fprintf(stderr, "IP_SOCK interface not initialized. Exiting.\n");
exit(-1); Document document;
if (document.Parse((char*)rx_buf->begin()).HasParseError() || document.IsObject() == false) {
log->error_hex(rx_buf->begin(), n, "Error parsing incoming data.\n");
return SRSLTE_ERROR;
} }
// open TCP socket // Pretty-print
if (srslte_netsource_init(&net_source, net_ip.c_str(), net_port, SRSLTE_NETSOURCE_TCP)) { StringBuffer buffer;
fprintf(stderr, "Error creating input TCP socket at port %d\n", net_port); PrettyWriter<StringBuffer> writer(buffer);
exit(-1); document.Accept(writer);
log->info("Received %d bytes\n%s\n", n, (char*)buffer.GetString());
// Get message
if (document.HasMember("CTRL")) {
log->info("Received CTRL command.\n");
handle_ctrl(document);
} else {
log->error("Received unknown request.\n");
} }
log->info("Listening on %s:%d for incoming connections ..\n", net_ip.c_str(), net_port); return SRSLTE_SUCCESS;
running = true;
int n;
while (run_enable) {
log->debug("Reading from IP_SOCK port ..\n");
n = srslte_netsource_read(&net_source, rx_buf->begin(), RX_BUF_SIZE);
if (n > 0) {
rx_buf->at(n) = '\0';
Document document;
if (document.Parse((char*)rx_buf->begin()).HasParseError()) {
log->error_hex(rx_buf->begin(), n, "Error parsing incoming data.\n");
break;
}
assert(document.IsObject());
// Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info("Received %d bytes\n%s\n", n, (char*)buffer.GetString());
// Get message
if (document.HasMember("CTRL")) {
log->info("Received CTRL command.\n");
handle_ctrl(document);
} else {
log->error("Received unknown request.\n");
}
} else if (n == 0) {
log->error("Receiving null from network\n");
} else {
log->error("Error receiving from network\n");
// exit(-1);
}
}
running = false;
srslte_netsource_free(&net_source);
} }
void handle_ctrl(Document& document) void handle_ctrl(Document& document)
@ -127,9 +101,7 @@ private:
string resp = ttcn3_helpers::get_ctrl_cnf(protocol.GetString(), ip_version, addr.GetString()); string resp = ttcn3_helpers::get_ctrl_cnf(protocol.GetString(), ip_version, addr.GetString());
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length()); log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
if (srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length()) != SRSLTE_SUCCESS) { send((const uint8_t*)resp.c_str(), resp.length());
log->error("Error sending message to tester.\n");
}
} }
}; };

View File

@ -0,0 +1,224 @@
/*
* Copyright 2013-2019 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
/*! \brief This class provides a common function for all TTCN3
* ports for SCTP socket creation, notification handling, etc.
*
*/
#ifndef SRSUE_TTCN3_PORT_HANDLER_H
#define SRSUE_TTCN3_PORT_HANDLER_H
#include "srslte/common/epoll_helper.h"
#include "srslte/common/log.h"
#include "ttcn3_common.h"
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/sctp.h>
class ttcn3_port_handler : public epoll_handler
{
public:
ttcn3_port_handler() : rx_buf(unique_byte_array_t(new byte_array_t)) {}
virtual ~ttcn3_port_handler() {}
virtual int handle_message(const unique_byte_array_t& rx_buf, const uint32_t n) = 0;
int handle_event(int fd, epoll_event e, int epoll_fd)
{
if (e.events & EPOLLIN) {
struct sctp_sndrcvinfo sri = {};
socklen_t fromlen = sizeof(client_addr);
int msg_flags = 0;
int rd_sz =
sctp_recvmsg(fd, rx_buf->begin(), RX_BUF_SIZE, (struct sockaddr*)&client_addr, &fromlen, &sri, &msg_flags);
if (rd_sz == -1 && errno != EAGAIN) {
log->error("Error reading from SCTP socket: %s", strerror(errno));
} else if (rd_sz == -1 && errno == EAGAIN) {
log->debug("Socket timeout reached");
} else {
if (msg_flags & MSG_NOTIFICATION) {
// Received notification
handle_notification(rx_buf->begin(), rd_sz);
} else {
// Received data
rx_buf->at(rd_sz) = '\0'; // Terminate buffer
handle_message(rx_buf, rd_sz);
}
}
}
return SRSLTE_SUCCESS;
}
int handle_notification(const uint8_t* payload, const uint32_t len)
{
union sctp_notification* notif = (union sctp_notification*)payload;
uint32_t notif_header_size = sizeof(((union sctp_notification*)NULL)->sn_header);
if (notif_header_size > len) {
printf("Error: Notification msg size is smaller than notification header size!\n");
return SRSLTE_ERROR;
}
switch (notif->sn_header.sn_type) {
case SCTP_ASSOC_CHANGE: {
if (sizeof(struct sctp_assoc_change) > len) {
printf("Error notification msg size is smaller than struct sctp_assoc_change size\n");
return SRSLTE_ERROR;
}
const char* state = NULL;
struct sctp_assoc_change* n = &notif->sn_assoc_change;
switch (n->sac_state) {
case SCTP_COMM_UP:
state = "COMM UP";
break;
case SCTP_COMM_LOST:
state = "COMM_LOST";
break;
case SCTP_RESTART:
state = "RESTART";
break;
case SCTP_SHUTDOWN_COMP:
state = "SHUTDOWN_COMP";
break;
case SCTP_CANT_STR_ASSOC:
state = "CAN'T START ASSOC";
break;
}
log->debug(
"SCTP_ASSOC_CHANGE notif: state: %s, error code: %d, out streams: %d, in streams: %d, assoc id: %d\n",
state,
n->sac_error,
n->sac_outbound_streams,
n->sac_inbound_streams,
n->sac_assoc_id);
break;
}
case SCTP_SHUTDOWN_EVENT: {
if (sizeof(struct sctp_shutdown_event) > len) {
printf("Error notification msg size is smaller than struct sctp_assoc_change size\n");
return SRSLTE_ERROR;
}
struct sctp_shutdown_event* n = &notif->sn_shutdown_event;
log->debug("SCTP_SHUTDOWN_EVENT notif: assoc id: %d\n", n->sse_assoc_id);
break;
}
default:
log->warning("Unhandled notification type %d\n", notif->sn_header.sn_type);
break;
}
return SRSLTE_SUCCESS;
}
///< Send buffer to tester
void send(const uint8_t* buffer, const uint32_t len)
{
if (sendto(sock_fd, buffer, len, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)) == -1) {
log->error("Error sending message to tester.\n");
}
}
///< Set socket to non-blocking-mode
int set_non_blocking(uint32_t fd)
{
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
perror("fcntl");
return SRSLTE_ERROR;
}
flags |= O_NONBLOCK;
int s = fcntl(fd, F_SETFL, flags);
if (s == -1) {
perror("fcntl");
return SRSLTE_ERROR;
}
return SRSLTE_SUCCESS;
}
///< Create, bind and listen on SCTP socket
int port_listen()
{
int ret = SRSLTE_ERROR;
sock_fd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
if (sock_fd == -1) {
log->console("Could not create SCTP socket\n");
return ret;
}
// Sets the data_io_event to be able to use sendrecv_info
// Subscribes to the SCTP_SHUTDOWN event, to handle graceful shutdown
struct sctp_event_subscribe events = {};
events.sctp_data_io_event = 1;
events.sctp_shutdown_event = 1;
events.sctp_association_event = 1;
if (setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events))) {
close(sock_fd);
log->console("Subscribing to sctp_data_io_events failed\n");
return SRSLTE_ERROR;
}
// Port bind
struct sockaddr_in bind_addr = {};
bind_addr.sin_family = AF_INET;
inet_pton(AF_INET, net_ip.c_str(), &(bind_addr.sin_addr));
bind_addr.sin_port = htons(net_port);
int one = 1;
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
ret = bind(sock_fd, (struct sockaddr*)&bind_addr, sizeof(bind_addr));
if (ret != 0) {
close(sock_fd);
log->error("Error binding SCTP socket\n");
log->console("Error binding SCTP socket\n");
return SRSLTE_ERROR;
}
// Listen for connections
ret = listen(sock_fd, SOMAXCONN);
if (ret != SRSLTE_SUCCESS) {
close(sock_fd);
log->error("Error in SCTP socket listen\n");
log->console("Error in SCTP socket listen\n");
return SRSLTE_ERROR;
}
set_non_blocking(sock_fd);
return sock_fd;
}
bool initialized = false;
std::string net_ip = "0.0.0.0";
uint32_t net_port = 0;
int sock_fd = -1;
struct sockaddr client_addr = {};
srslte::log* log = nullptr;
unique_byte_array_t rx_buf; ///< Receive buffer for this port
};
#endif // SRSUE_TTCN3_PORT_HANDLER_H

View File

@ -26,120 +26,87 @@
#include "srslte/common/common.h" #include "srslte/common/common.h"
#include "srslte/common/pdu.h" #include "srslte/common/pdu.h"
#include "ttcn3_interfaces.h" #include "ttcn3_interfaces.h"
#include "ttcn3_port_handler.h"
#include <srslte/interfaces/ue_interfaces.h> #include <srslte/interfaces/ue_interfaces.h>
using namespace srslte; using namespace srslte;
// The SRB interface // The SRB interface
class ttcn3_srb_interface : public netsource_handler class ttcn3_srb_interface : public ttcn3_port_handler
{ {
public: public:
ttcn3_srb_interface() : pool(byte_buffer_pool::get_instance()), netsource_handler("TTCN3_SRB_IF"){}; ttcn3_srb_interface() : pool(byte_buffer_pool::get_instance()) {}
~ttcn3_srb_interface(){}; ~ttcn3_srb_interface() = default;
void init(ss_srb_interface* syssim_, srslte::log* log_, std::string net_ip_, uint32_t net_port_) int init(ss_srb_interface* syssim_, srslte::log* log_, std::string net_ip_, uint32_t net_port_)
{ {
syssim = syssim_; syssim = syssim_;
log = log_; log = log_;
net_ip = net_ip_; net_ip = net_ip_;
net_port = net_port_; net_port = net_port_;
initialized = true; initialized = true;
log->debug("Initialized.\n"); log->debug("Initialized.\n");
return port_listen();
} }
void tx(unique_byte_buffer_t pdu) void tx(unique_byte_buffer_t pdu)
{ {
if (running) { if (initialized) {
log->info_hex(pdu->msg, pdu->N_bytes, "Sending %d B to Titan\n", pdu->N_bytes); log->info_hex(pdu->msg, pdu->N_bytes, "Sending %d B to Titan\n", pdu->N_bytes);
if (srslte_netsource_write(&net_source, (void*)pdu->msg, pdu->N_bytes) != SRSLTE_SUCCESS) { send(pdu->msg, pdu->N_bytes);
log->error("Error sending message to tester.\n");
};
} else { } else {
log->error("Trying to transmit but port not connected.\n"); log->error("Trying to transmit but port not connected.\n");
} }
} }
private: private:
void run_thread() ///< Main message handler
int handle_message(const unique_byte_array_t& rx_buf, const uint32_t n)
{ {
if (!initialized) { log->debug_hex(rx_buf->begin(), n, "Received %d B from remote.\n", n);
fprintf(stderr, "SRB interface not initialized. Exiting.\n");
exit(-1); // Chop incoming msg, first two bytes are length of the JSON
// (see IPL4_EUTRA_SYSTEM_Definitions.ttcn
uint16_t json_len = ((uint16_t)rx_buf->at(0) << 8) | rx_buf->at(1);
// The data part after the JSON starts right here but handling
// is done in the respective functions
uint16_t rx_buf_offset = json_len + 2;
Document document;
if (document.Parse((char*)&rx_buf->at(2)).HasParseError() || document.IsObject() == false) {
log->error_hex((uint8*)&rx_buf->at(2), json_len, "Error parsing incoming data.\n");
return SRSLTE_ERROR;
} }
// open TCP socket // Pretty-print
if (srslte_netsource_init(&net_source, net_ip.c_str(), net_port, SRSLTE_NETSOURCE_TCP)) { StringBuffer buffer;
fprintf(stderr, "Error creating input TCP socket at port %d\n", net_port); PrettyWriter<StringBuffer> writer(buffer);
exit(-1); document.Accept(writer);
log->info("Received JSON with %d B\n%s\n", json_len, (char*)buffer.GetString());
// check for common
assert(document.HasMember("Common"));
assert(document["Common"].IsObject());
// Check for request type
assert(document.HasMember("RrcPdu"));
assert(document["RrcPdu"].IsObject());
// Get request type
const Value& rrcpdu = document["RrcPdu"];
if (rrcpdu.HasMember("Ccch")) {
rx_buf_offset += 2;
handle_ccch_pdu(document, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else if (rrcpdu.HasMember("Dcch")) {
rx_buf_offset += 2;
uint32_t lcid = document["Common"]["RoutingInfo"]["RadioBearerId"]["Srb"].GetInt();
handle_dcch_pdu(document, lcid, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else {
log->error("Received unknown request.\n");
} }
running = true; return SRSLTE_SUCCESS;
int n;
while (run_enable) {
log->debug("Reading from SRB port ..\n");
n = srslte_netsource_read(&net_source, rx_buf->begin(), RX_BUF_SIZE);
if (n > 0) {
rx_buf->at(n) = '\0';
log->debug_hex(rx_buf->begin(), n, "Received %d B from remote.\n", n);
// Chop incoming msg, first two bytes are length of the JSON
// (see IPL4_EUTRA_SYSTEM_Definitions.ttcn
uint16_t json_len = ((uint16_t)rx_buf->at(0) << 8) | rx_buf->at(1);
// Copy JSON from received buffer and null-terminate
char json[json_len + 1];
memcpy(json, &rx_buf->at(2), json_len);
json[json_len] = '\0';
// The data part after the JSON starts right here but handling
// is done in the respective functions
uint16_t rx_buf_offset = json_len + 2;
Document document;
if (document.Parse(json).HasParseError()) {
log->error_hex((uint8*)json, json_len, "Error parsing incoming data.\n");
break;
}
assert(document.IsObject());
// Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info("Received JSON with %d B\n%s\n", json_len, (char*)buffer.GetString());
// check for common
assert(document.HasMember("Common"));
assert(document["Common"].IsObject());
// Check for request type
assert(document.HasMember("RrcPdu"));
assert(document["RrcPdu"].IsObject());
// Get request type
const Value& rrcpdu = document["RrcPdu"];
if (rrcpdu.HasMember("Ccch")) {
rx_buf_offset += 2;
handle_ccch_pdu(document, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else if (rrcpdu.HasMember("Dcch")) {
rx_buf_offset += 2;
uint32_t lcid = document["Common"]["RoutingInfo"]["RadioBearerId"]["Srb"].GetInt();
handle_dcch_pdu(document, lcid, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else {
log->error("Received unknown request.\n");
}
} else if (n == 0) {
log->error("Receiving null from network\n");
} else {
log->error("Error receiving from network\n");
}
}
running = false;
srslte_netsource_free(&net_source);
} }
// Todo: move to SYSSIM // Todo: move to SYSSIM
@ -188,6 +155,10 @@ private:
ss_srb_interface* syssim = nullptr; ss_srb_interface* syssim = nullptr;
byte_buffer_pool* pool = nullptr; byte_buffer_pool* pool = nullptr;
int srb_fd = 0;
// struct sctp_sndrcvinfo sri = {};
// struct sockaddr_in client_addr;
}; };
#endif // SRSUE_TTCN3_SRB_INTERFACE_H #endif // SRSUE_TTCN3_SRB_INTERFACE_H

View File

@ -29,13 +29,13 @@
using namespace srslte; using namespace srslte;
// The EUTRA.SYS interface // The EUTRA.SYS interface
class ttcn3_sys_interface : public netsource_handler class ttcn3_sys_interface : public ttcn3_port_handler
{ {
public: public:
ttcn3_sys_interface() : netsource_handler("TTCN3_SYS_IF"){}; ttcn3_sys_interface(){};
~ttcn3_sys_interface(){}; ~ttcn3_sys_interface(){};
void init(ss_sys_interface* syssim_, srslte::log* log_, std::string net_ip_, uint32_t net_port_) int init(ss_sys_interface* syssim_, srslte::log* log_, std::string net_ip_, uint32_t net_port_)
{ {
syssim = syssim_; syssim = syssim_;
net_ip = net_ip_; net_ip = net_ip_;
@ -44,9 +44,81 @@ public:
initialized = true; initialized = true;
log->debug("Initialized.\n"); log->debug("Initialized.\n");
pool = byte_buffer_pool::get_instance(); pool = byte_buffer_pool::get_instance();
return port_listen();
} }
private: private:
///< Main message handler
int handle_message(const unique_byte_array_t& rx_buf, const uint32_t n)
{
log->debug("Received %d B from remote.\n", n);
// Chop incoming msg, first two bytes are length of the JSON
// (see IPL4_EUTRA_SYSTEM_Definitions.ttcn
uint16_t json_len = ((uint16_t)rx_buf->at(0) << 8) | rx_buf->at(1);
// Copy JSON from received buffer and null-terminate
char json[json_len + 1];
memcpy(json, &rx_buf->at(2), json_len);
json[json_len] = '\0';
// The data part after the JSON starts right here but handling
// is done in the respective functions
uint16_t rx_buf_offset = json_len + 2;
Document document;
if (document.Parse(json).HasParseError() || document.IsObject() == false) {
log->error_hex((uint8*)json, json_len, "Error parsing incoming data.\n");
return SRSLTE_ERROR;
}
// Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info_long("Received %d bytes\n%s\n", json_len, (char*)buffer.GetString());
// check for common
assert(document.HasMember("Common"));
assert(document["Common"].IsObject());
// Check for request type
assert(document.HasMember("Request"));
assert(document["Request"].IsObject());
// Get request type
const Value& request = document["Request"];
if (request.HasMember("Cell")) {
log->info("Received Cell request.\n");
handle_request_cell(document, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else if (request.HasMember("L1MacIndCtrl")) {
log->info("Received L1MacIndCtrl request.\n");
handle_request_l1_mac_ind_ctrl(document);
} else if (request.HasMember("RadioBearerList")) {
log->info("Received RadioBearerList request.\n");
handle_request_radio_bearer_list(document);
} else if (request.HasMember("CellAttenuationList")) {
log->info("Received CellAttenuationList request.\n");
handle_request_cell_attenuation_list(document);
} else if (request.HasMember("PdcpCount")) {
log->info("Received PdcpCount request.\n");
handle_request_pdcp_count(document);
} else if (request.HasMember("AS_Security")) {
log->info("Received AS_Security request.\n");
handle_request_as_security(document);
} else if (request.HasMember("EnquireTiming")) {
log->info("Received EnquireTiming request.\n");
handle_request_enquire_timing(document);
} else if (request.HasMember("Paging")) {
log->info("Received Paging request.\n");
handle_request_paging(document, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else {
log->error("Received unknown request.\n");
}
return SRSLTE_SUCCESS;
}
void handle_request_cell_basic(Document& document, const uint8_t* payload, const uint16_t len) void handle_request_cell_basic(Document& document, const uint8_t* payload, const uint16_t len)
{ {
if (document["Request"]["Cell"]["AddOrReconfigure"]["Basic"].HasMember("StaticCellInfo")) { if (document["Request"]["Cell"]["AddOrReconfigure"]["Basic"].HasMember("StaticCellInfo")) {
@ -113,9 +185,7 @@ private:
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_name.GetString(), "Cell"); std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_name.GetString(), "Cell");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length()); log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
if (srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length()) != SRSLTE_SUCCESS) { send((const uint8_t*)resp.c_str(), resp.length());
log->error("Error sending message to tester.\n");
}
} }
} }
@ -127,9 +197,7 @@ private:
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id, "Cell"); std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id, "Cell");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length()); log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
if (srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length()) != SRSLTE_SUCCESS) { send((const uint8_t*)resp.c_str(), resp.length());
log->error("Error sending message to tester.\n");
}
} }
void handle_request_cell(Document& document, const uint8_t* payload, const uint16_t len) void handle_request_cell(Document& document, const uint8_t* payload, const uint16_t len)
@ -187,9 +255,7 @@ private:
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "L1MacIndCtrl"); std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "L1MacIndCtrl");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length()); log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
if (srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length()) != SRSLTE_SUCCESS) { send((const uint8_t*)resp.c_str(), resp.length());
log->error("Error sending message to tester.\n");
}
} }
} }
@ -248,9 +314,7 @@ private:
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "RadioBearerList"); std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "RadioBearerList");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length()); log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
if (srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length()) != SRSLTE_SUCCESS) { send((const uint8_t*)resp.c_str(), resp.length());
log->error("Error sending message to tester.\n");
}
} }
void handle_request_cell_attenuation_list(Document& document) void handle_request_cell_attenuation_list(Document& document)
@ -299,9 +363,7 @@ private:
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "CellAttenuationList"); std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "CellAttenuationList");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length()); log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
if (srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length()) != SRSLTE_SUCCESS) { send((const uint8_t*)resp.c_str(), resp.length());
log->error("Error sending message to tester.\n");
}
} }
void handle_request_pdcp_count(Document& document) void handle_request_pdcp_count(Document& document)
@ -338,9 +400,7 @@ private:
std::string resp = ttcn3_helpers::get_pdcp_count_response(cell_id.GetString(), bearers); std::string resp = ttcn3_helpers::get_pdcp_count_response(cell_id.GetString(), bearers);
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length()); log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
if (srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length()) != SRSLTE_SUCCESS) { send((const uint8_t*)resp.c_str(), resp.length());
log->error("Error sending message to tester.\n");
}
} }
void handle_request_as_security(Document& document) void handle_request_as_security(Document& document)
@ -429,9 +489,7 @@ private:
if (config_flag.GetBool() == true) { if (config_flag.GetBool() == true) {
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "AS_Security"); std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "AS_Security");
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length()); log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
if (srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length()) != SRSLTE_SUCCESS) { send((const uint8_t*)resp.c_str(), resp.length());
log->error("Error sending message to tester.\n");
}
} else { } else {
log->info("Skipping response for AS_Security message.\n"); log->info("Skipping response for AS_Security message.\n");
} }
@ -469,9 +527,7 @@ private:
ttcn3_helpers::get_sys_req_cnf_with_time(cell_id.GetString(), "EnquireTiming", syssim->get_tti()); ttcn3_helpers::get_sys_req_cnf_with_time(cell_id.GetString(), "EnquireTiming", syssim->get_tti());
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length()); log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
if (srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length()) != SRSLTE_SUCCESS) { send((const uint8_t*)resp.c_str(), resp.length());
log->error("Error sending message to tester.\n");
}
} }
void handle_request_paging(Document& document, const uint8_t* payload, const uint16_t len) void handle_request_paging(Document& document, const uint8_t* payload, const uint16_t len)
@ -512,112 +568,12 @@ private:
std::string resp = ttcn3_helpers::get_sys_req_cnf_with_time(cell_id.GetString(), "Paging", syssim->get_tti()); std::string resp = ttcn3_helpers::get_sys_req_cnf_with_time(cell_id.GetString(), "Paging", syssim->get_tti());
log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length()); log->info("Sending %s to tester (%zd B)\n", resp.c_str(), resp.length());
if (srslte_netsource_write(&net_source, (char*)resp.c_str(), resp.length()) != SRSLTE_SUCCESS) { send((const uint8_t*)resp.c_str(), resp.length());
log->error("Error sending message to tester.\n");
}
} else { } else {
log->info("Skipping response for Paging message.\n"); log->info("Skipping response for Paging message.\n");
} }
} }
void run_thread()
{
if (!initialized) {
fprintf(stderr, "SYS interface not initialized. Exiting.\n");
exit(-1);
}
// open TCP socket
if (srslte_netsource_init(&net_source, net_ip.c_str(), net_port, SRSLTE_NETSOURCE_TCP)) {
fprintf(stderr, "Error creating input TCP socket at port %d\n", net_port);
exit(-1);
}
log->info("Listening on %s:%d for incoming connections ..\n", net_ip.c_str(), net_port);
running = true;
int n;
while (run_enable) {
log->debug("Reading from SYS port ..\n");
n = srslte_netsource_read(&net_source, rx_buf->begin(), RX_BUF_SIZE);
if (n > 0) {
rx_buf->at(n) = '\0';
log->debug("Received %d B from remote.\n", n);
// Chop incoming msg, first two bytes are length of the JSON
// (see IPL4_EUTRA_SYSTEM_Definitions.ttcn
uint16_t json_len = ((uint16_t)rx_buf->at(0) << 8) | rx_buf->at(1);
// Copy JSON from received buffer and null-terminate
char json[json_len + 1];
memcpy(json, &rx_buf->at(2), json_len);
json[json_len] = '\0';
// The data part after the JSON starts right here but handling
// is done in the respective functions
uint16_t rx_buf_offset = json_len + 2;
Document document;
if (document.Parse(json).HasParseError()) {
log->error_hex((uint8*)json, json_len, "Error parsing incoming data.\n");
break;
}
assert(document.IsObject());
// Pretty-print
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info_long("Received %d bytes\n%s\n", json_len, (char*)buffer.GetString());
// check for common
assert(document.HasMember("Common"));
assert(document["Common"].IsObject());
// Check for request type
assert(document.HasMember("Request"));
assert(document["Request"].IsObject());
// Get request type
const Value& request = document["Request"];
if (request.HasMember("Cell")) {
log->info("Received Cell request.\n");
handle_request_cell(document, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else if (request.HasMember("L1MacIndCtrl")) {
log->info("Received L1MacIndCtrl request.\n");
handle_request_l1_mac_ind_ctrl(document);
} else if (request.HasMember("RadioBearerList")) {
log->info("Received RadioBearerList request.\n");
handle_request_radio_bearer_list(document);
} else if (request.HasMember("CellAttenuationList")) {
log->info("Received CellAttenuationList request.\n");
handle_request_cell_attenuation_list(document);
} else if (request.HasMember("PdcpCount")) {
log->info("Received PdcpCount request.\n");
handle_request_pdcp_count(document);
} else if (request.HasMember("AS_Security")) {
log->info("Received AS_Security request.\n");
handle_request_as_security(document);
} else if (request.HasMember("EnquireTiming")) {
log->info("Received EnquireTiming request.\n");
handle_request_enquire_timing(document);
} else if (request.HasMember("Paging")) {
log->info("Received Paging request.\n");
handle_request_paging(document, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
} else {
log->error("Received unknown request.\n");
}
} else {
log->error("Error receiving from network\n");
}
}
running = false;
srslte_netsource_free(&net_source);
}
phy_interface_syssim* phy = nullptr; phy_interface_syssim* phy = nullptr;
ss_sys_interface* syssim = nullptr; ss_sys_interface* syssim = nullptr;
byte_buffer_pool* pool = nullptr; byte_buffer_pool* pool = nullptr;

View File

@ -23,24 +23,23 @@
#define SRSUE_TTCN3_SYSSIM_H #define SRSUE_TTCN3_SYSSIM_H
#include "dut_utils.h" #include "dut_utils.h"
#include "srslte/common/netsource_handler.h"
#include "srslte/common/pdu_queue.h" #include "srslte/common/pdu_queue.h"
#include "srslte/common/threads.h" #include "srslte/common/threads.h"
#include "srslte/upper/pdcp.h" #include "srslte/upper/pdcp.h"
#include "srslte/upper/rlc.h" #include "srslte/upper/rlc.h"
#include "ttcn3_common.h"
#include "ttcn3_ip_ctrl_interface.h" #include "ttcn3_ip_ctrl_interface.h"
#include "ttcn3_ip_sock_interface.h" #include "ttcn3_ip_sock_interface.h"
#include "ttcn3_srb_interface.h" #include "ttcn3_srb_interface.h"
#include "ttcn3_sys_interface.h" #include "ttcn3_sys_interface.h"
#include "ttcn3_ue.h" #include "ttcn3_ue.h"
#include "ttcn3_ut_interface.h" #include "ttcn3_ut_interface.h"
#include <functional>
#include <srslte/interfaces/ue_interfaces.h> #include <srslte/interfaces/ue_interfaces.h>
#define TTCN3_CRNTI (0x1001) #define TTCN3_CRNTI (0x1001)
class ttcn3_syssim : public thread, class ttcn3_syssim : public syssim_interface_phy,
public syssim_interface_phy,
public ss_ut_interface, public ss_ut_interface,
public ss_sys_interface, public ss_sys_interface,
public ss_srb_interface, public ss_srb_interface,
@ -50,7 +49,7 @@ class ttcn3_syssim : public thread,
public srslte::pdu_queue::process_callback public srslte::pdu_queue::process_callback
{ {
public: public:
ttcn3_syssim(srslte::logger_file* logger_file_) : ttcn3_syssim(srslte::logger_file* logger_file_, ttcn3_ue* ue_) :
mac_msg_ul(20, &ss_mac_log), mac_msg_ul(20, &ss_mac_log),
mac_msg_dl(20, &ss_mac_log), mac_msg_dl(20, &ss_mac_log),
timers(8), timers(8),
@ -58,16 +57,16 @@ public:
logger(logger_file_), logger(logger_file_),
logger_file(logger_file_), logger_file(logger_file_),
pool(byte_buffer_pool::get_instance()), pool(byte_buffer_pool::get_instance()),
thread("TTCN3_SYSSIM"), ue(ue_),
rlc(&ss_rlc_log), rlc(&ss_rlc_log),
signal_handler(&running),
timer_handler(create_tti_timer(), [&](uint64_t res) { new_tti_indication(res); }),
pdcp(&timers, &ss_pdcp_log){}; pdcp(&timers, &ss_pdcp_log){};
~ttcn3_syssim(){}; ~ttcn3_syssim(){};
void init(const all_args_t& args_) int init(const all_args_t& args_)
{ {
std::lock_guard<std::mutex> lock(mutex);
args = args_; args = args_;
// Make sure to get SS logging as well // Make sure to get SS logging as well
@ -106,40 +105,236 @@ public:
ss_rlc_log.set_hex_limit(args.log.all_hex_limit); ss_rlc_log.set_hex_limit(args.log.all_hex_limit);
ss_pdcp_log.set_hex_limit(args.log.all_hex_limit); ss_pdcp_log.set_hex_limit(args.log.all_hex_limit);
// Init epoll socket and add FDs
epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
log.error("Error creating epoll\n");
return SRSLTE_ERROR;
}
// add signalfd
signal_fd = add_signalfd();
if (add_epoll(signal_fd, epoll_fd) != SRSLTE_SUCCESS) {
log.error("Error while adding signalfd to epoll\n");
return SRSLTE_ERROR;
}
event_handler.insert({signal_fd, &signal_handler});
// init system interfaces to tester // init system interfaces to tester
ut.init(this, &ut_log, "0.0.0.0", 2222); if (add_port_handler() != SRSLTE_SUCCESS) {
sys.init(this, &sys_log, "0.0.0.0", 2223); log.error("Error creating port handlers\n");
ip_sock.init(&ip_sock_log, "0.0.0.0", 2224); return SRSLTE_ERROR;
ip_ctrl.init(&ip_ctrl_log, "0.0.0.0", 2225); }
srb.init(this, &srb_log, "0.0.0.0", 2226);
ut.start(-2);
sys.start(-2);
ip_sock.start(-2);
ip_ctrl.start(-2);
srb.start(-2);
// Init SS layers
pdus.init(this, &log); pdus.init(this, &log);
rlc.init(&pdcp, this, &timers, 0 /* RB_ID_SRB0 */); rlc.init(&pdcp, this, &timers, 0 /* RB_ID_SRB0 */);
pdcp.init(&rlc, this, nullptr); pdcp.init(&rlc, this, nullptr);
return SRSLTE_SUCCESS;
}
int add_port_handler()
{
// UT port
int ut_fd = ut.init(this, &ut_log, listen_address, UT_PORT);
if (add_epoll(ut_fd, epoll_fd) != SRSLTE_SUCCESS) {
log.error("Error while adding UT port to epoll\n");
return SRSLTE_ERROR;
}
event_handler.insert({ut_fd, &ut});
log.console("UT handler listening on SCTP port %d\n", UT_PORT);
// SYS port
int sys_fd = sys.init(this, &sys_log, listen_address, SYS_PORT);
if (add_epoll(sys_fd, epoll_fd) != SRSLTE_SUCCESS) {
log.error("Error while adding SYS port to epoll\n");
return SRSLTE_ERROR;
}
event_handler.insert({sys_fd, &sys});
log.console("SYS handler listening on SCTP port %d\n", SYS_PORT);
// IPsock port
int ip_sock_fd = ip_sock.init(&ip_sock_log, listen_address, IPSOCK_PORT);
if (add_epoll(ip_sock_fd, epoll_fd) != SRSLTE_SUCCESS) {
log.error("Error while adding IP sock port to epoll\n");
return SRSLTE_ERROR;
}
event_handler.insert({ip_sock_fd, &ip_sock});
log.console("IPSOCK handler listening on SCTP port %d\n", IPSOCK_PORT);
// IPctrl port
int ip_ctrl_fd = ip_ctrl.init(&ip_ctrl_log, listen_address, IPCTRL_PORT);
if (add_epoll(ip_ctrl_fd, epoll_fd) != SRSLTE_SUCCESS) {
log.error("Error while adding IP ctrl port to epoll\n");
return SRSLTE_ERROR;
}
event_handler.insert({ip_ctrl_fd, &ip_ctrl});
log.console("IPCTRL handler listening on SCTP port %d\n", IPCTRL_PORT);
// add SRB fd
int srb_fd = srb.init(this, &srb_log, listen_address, SRB_PORT);
if (add_epoll(srb_fd, epoll_fd) != SRSLTE_SUCCESS) {
log.error("Error while adding SRB port to epoll\n");
return SRSLTE_ERROR;
}
event_handler.insert({srb_fd, &srb});
log.console("SRB handler listening on SCTP port %d\n", SRB_PORT);
return SRSLTE_SUCCESS;
}
///< Function called by epoll timer handler when TTI timer expires
void new_tti_indication(uint64_t res)
{
tti = (tti + 1) % 10240;
log.step(tti);
log.debug("Start new TTI\n");
ue->set_current_tti(tti);
// process events, if any
while (not event_queue.empty()) {
ss_events_t ev = event_queue.wait_pop();
switch (ev) {
case UE_SWITCH_ON:
log.console("Switching on UE ID=%d\n", run_id);
ue->switch_on();
break;
case UE_SWITCH_OFF:
log.console("Switching off UE ID=%d\n", run_id);
ue->switch_off();
break;
case ENABLE_DATA:
log.console("Enabling data for UE ID=%d\n", run_id);
ue->enable_data();
break;
case DISABLE_DATA:
log.console("Disabling data for UE ID=%d\n", run_id);
ue->disable_data();
break;
}
}
if (pcell_idx == -1) {
log.debug("Skipping TTI. Pcell not yet selected.\n");
return;
}
// DL/UL processing if UE has selected cell
dl_rnti = ue->get_dl_sched_rnti(tti);
if (SRSLTE_RNTI_ISSI(dl_rnti)) {
// deliver SIBs one after another
mac_interface_phy_lte::mac_grant_dl_t dl_grant = {};
dl_grant.pid = get_pid(tti);
dl_grant.rnti = dl_rnti;
dl_grant.tb[0].tbs = cells[pcell_idx]->sibs[cells[pcell_idx]->sib_idx]->N_bytes;
dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti);
ue->new_tb(dl_grant, cells[pcell_idx]->sibs[cells[pcell_idx]->sib_idx]->msg);
log.info("Delivered SIB%d for pcell_idx=%d\n", cells[pcell_idx]->sib_idx, pcell_idx);
cells[pcell_idx]->sib_idx = (cells[pcell_idx]->sib_idx + 1) % cells[pcell_idx]->sibs.size();
} else if (SRSLTE_RNTI_ISRAR(dl_rnti)) {
if (prach_tti != -1) {
rar_tti = (prach_tti + 3) % 10240;
if (tti == rar_tti) {
send_rar(prach_preamble_index);
}
}
} else if (SRSLTE_RNTI_ISPA(dl_rnti)) {
log.debug("Searching for paging RNTI\n");
// PCH will be triggered from SYSSIM after receiving Paging
} else if (SRSLTE_RNTI_ISUSER(dl_rnti)) {
// check if this is for contention resolution after PRACH/RAR
if (dl_rnti == crnti) {
log.debug("Searching for C-RNTI=%d\n", crnti);
if (rar_tti != -1) {
msg3_tti = (rar_tti + 3) % 10240;
if (tti == msg3_tti) {
send_msg3_grant();
rar_tti = -1;
}
}
}
// check for SR
if (sr_tti != -1) {
send_sr_ul_grant();
}
if (dl_rnti != SRSLTE_INVALID_RNTI) {
log.debug("Searching for RNTI=%d\n", dl_rnti);
// look for DL data to be send in each bearer and provide grant accordingly
for (int lcid = 0; lcid < SRSLTE_N_RADIO_BEARERS; lcid++) {
uint32_t buf_state = rlc.get_buffer_state(lcid);
if (buf_state > 0) {
log.debug("LCID=%d, buffer_state=%d\n", lcid, buf_state);
const uint32_t mac_header_size = 10; // Add MAC header (10 B for all subheaders, etc)
if (tmp_rlc_buffer.get_tailroom() > (buf_state + mac_header_size)) {
uint32_t pdu_size = rlc.read_pdu(lcid, tmp_rlc_buffer.msg, buf_state);
tx_payload_buffer.clear();
mac_msg_dl.init_tx(&tx_payload_buffer, pdu_size + mac_header_size, false);
// check if this is Msg4 that needs to contain the contention resolution ID CE
if (msg3_tti != -1) {
if (lcid == 0) {
if (mac_msg_dl.new_subh()) {
if (mac_msg_dl.get()->set_con_res_id(conres_id)) {
log.info("CE: Added Contention Resolution ID=0x%" PRIx64 "\n", conres_id);
} else {
log.error("CE: Setting Contention Resolution ID CE\n");
}
} else {
log.error("CE: Setting Contention Resolution ID CE. No space for a subheader\n");
}
msg3_tti = -1;
}
}
// Add payload
if (mac_msg_dl.new_subh()) {
int n = mac_msg_dl.get()->set_sdu(lcid, pdu_size, tmp_rlc_buffer.msg);
if (n == -1) {
log.error("Error while adding SDU (%d B) to MAC PDU\n", pdu_size);
mac_msg_dl.del_subh();
}
}
uint8_t* mac_pdu_ptr = mac_msg_dl.write_packet(&log);
if (mac_pdu_ptr != nullptr) {
log.info_hex(mac_pdu_ptr, mac_msg_dl.get_pdu_len(), "DL MAC PDU (%d B):\n", mac_msg_dl.get_pdu_len());
// Prepare MAC grant for CCCH
mac_interface_phy_lte::mac_grant_dl_t dl_grant = {};
dl_grant.pid = get_pid(tti);
dl_grant.rnti = dl_rnti;
dl_grant.tb[0].tbs = mac_msg_dl.get_pdu_len();
dl_grant.tb[0].ndi_present = true;
dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti);
ue->new_tb(dl_grant, (const uint8_t*)mac_pdu_ptr);
} else {
log.error("Error writing DL MAC PDU\n");
}
mac_msg_dl.reset();
} else {
log.error("Can't fit RLC PDU into buffer (%d > %d)\n", buf_state, tmp_rlc_buffer.get_tailroom());
}
}
}
// Check if we need to provide a UL grant as well
}
} else {
log.debug("Not handling RNTI=%d\n", dl_rnti);
}
} }
void stop() void stop()
{ {
std::lock_guard<std::mutex> lock(mutex);
running = false; running = false;
ue->stop();
if (ue != NULL) {
ue->stop();
}
// Stopping system interface
ut.stop();
sys.stop();
ip_sock.stop();
ip_ctrl.stop();
srb.stop();
} }
// Internal function called with acquired lock // Internal function called with acquired lock
@ -148,85 +343,66 @@ public:
rlc.reset(); rlc.reset();
pdcp.reset(); pdcp.reset();
cells.clear(); cells.clear();
pcell_idx = -1; pcell_idx = -1;
as_security_enabled = false; as_security_enabled = false;
} }
// Called from UT before starting testcase // Called from UT before starting testcase
void tc_start(const char* name) void tc_start(const char* name)
{ {
std::lock_guard<std::mutex> lock(mutex); // strip testsuite name
std::string tc_name = get_tc_name(name);
if (ue == nullptr) { // Make a copy of the UE args for this run
// strip testsuite name all_args_t local_args = args;
std::string tc_name = get_tc_name(name);
// Make a copy of the UE args for this run // set up logging
all_args_t local_args = args; if (args.log.filename == "stdout") {
logger = &logger_stdout;
// set up logging
if (args.log.filename == "stdout") {
logger = &logger_stdout;
} else {
logger_file->init(get_filename_with_tc_name(local_args.log.filename, run_id, tc_name).c_str(), -1);
logger = logger_file;
}
log.info("Initializing UE ID=%d for TC=%s\n", run_id, tc_name.c_str());
log.console("Initializing UE ID=%d for TC=%s\n", run_id, tc_name.c_str());
// Patch UE config
local_args.stack.pcap.filename = get_filename_with_tc_name(args.stack.pcap.filename, run_id, tc_name);
local_args.stack.pcap.nas_filename = get_filename_with_tc_name(args.stack.pcap.nas_filename, run_id, tc_name);
// bring up UE
ue = std::unique_ptr<ttcn3_ue>(new ttcn3_ue());
if (ue->init(local_args, logger, this, tc_name)) {
ue->stop();
ue.reset(nullptr);
std::string err("Couldn't initialize UE.\n");
log.error("%s\n", err.c_str());
log.console("%s\n", err.c_str());
return;
}
// Start simulator thread
running = true;
start();
} else { } else {
log.error("UE hasn't been deallocated properly because TC didn't finish correctly.\n"); logger_file->init(get_filename_with_tc_name(local_args.log.filename, run_id, tc_name).c_str(), -1);
log.console("UE hasn't been deallocated properly because TC didn't finish correctly.\n"); logger = logger_file;
} }
log.info("Initializing UE ID=%d for TC=%s\n", run_id, tc_name.c_str());
log.console("Initializing UE ID=%d for TC=%s\n", run_id, tc_name.c_str());
// Patch UE config
local_args.stack.pcap.filename = get_filename_with_tc_name(args.stack.pcap.filename, run_id, tc_name);
local_args.stack.pcap.nas_filename = get_filename_with_tc_name(args.stack.pcap.nas_filename, run_id, tc_name);
// bring up UE
if (ue->init(local_args, logger, this, tc_name)) {
ue->stop();
std::string err("Couldn't initialize UE.\n");
log.error("%s\n", err.c_str());
log.console("%s\n", err.c_str());
return;
}
// create and add TTI timer to epoll
if (add_epoll(timer_handler.get_timer_fd(), epoll_fd) != SRSLTE_SUCCESS) {
log.error("Error while adding TTI timer to epoll\n");
}
event_handler.insert({timer_handler.get_timer_fd(), &timer_handler});
} }
// Called from UT to terminate the testcase // Called from UT to terminate the testcase
void tc_end() void tc_end()
{ {
// ask periodic thread to stop before locking mutex log.info("Deinitializing UE ID=%d\n", run_id);
running = false; log.console("Deinitializing UE ID=%d\n", run_id);
ue->stop();
std::lock_guard<std::mutex> lock(mutex); // stop TTI timer
del_epoll(timer_handler.get_timer_fd(), epoll_fd);
if (ue != nullptr) { logger_file->stop();
log.info("Deinitializing UE ID=%d\n", run_id);
log.console("Deinitializing UE ID=%d\n", run_id);
ue->stop();
// wait until SS main thread has terminated before resetting UE run_id++;
wait_thread_finish();
ue.reset(); // Reset SS' RLC and PDCP
reset();
// Reset SS' RLC and PDCP
reset();
logger_file->stop();
run_id++;
} else {
log.error("UE is not allocated. Nothing needs to be done.\n");
log.console("UE is not allocated. Nothing needs to be done.\n");
}
} }
void power_off_ue() void power_off_ue()
@ -248,8 +424,7 @@ public:
{ {
// verify that UE intends to send PRACH on current Pcell // verify that UE intends to send PRACH on current Pcell
if (cells[pcell_idx]->cell.id != cell_id) { if (cells[pcell_idx]->cell.id != cell_id) {
log.error( log.error("UE is attempting to PRACH on pci=%d, current Pcell=%d\n", cell_id, cells[pcell_idx]->cell.id);
"UE is attempting to PRACH on pci=%d while current Pcell is pci=%d\n", cell_id, cells[pcell_idx]->cell.id);
return; return;
} }
@ -467,163 +642,42 @@ public:
return last_dl_ndi[pid]; return last_dl_ndi[pid];
} }
void run_thread() int run()
{ {
uint32_t sib_idx = 0; running = true;
while (running) { while (running) {
{ // wait for event
std::lock_guard<std::mutex> lock(mutex); const int32_t epoll_timeout_ms = -1;
const uint32_t MAX_EVENTS = 1;
struct epoll_event events[MAX_EVENTS] = {};
int nof_events = epoll_wait(epoll_fd, events, MAX_EVENTS, epoll_timeout_ms);
tti = (tti + 1) % 10240; // handle event
if (nof_events == -1) {
perror("epoll_wait() error");
break;
}
if (nof_events == 0) {
printf("time out %f sec expired\n", epoll_timeout_ms / 1000.0);
continue;
}
log.step(tti); for (int i = 0; i < nof_events; ++i) {
log.debug("Start new TTI\n"); if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) {
///< An error has occured on this fd, or the socket is not ready for reading
ue->set_current_tti(tti); fprintf(stderr, "epoll error\n");
close(events[i].data.fd);
// process events, if any
while (not event_queue.empty()) {
ss_events_t ev = event_queue.wait_pop();
switch (ev) {
case UE_SWITCH_ON:
log.console("Switching on UE ID=%d\n", run_id);
ue->switch_on();
break;
case UE_SWITCH_OFF:
log.console("Switching off UE ID=%d\n", run_id);
ue->switch_off();
break;
case ENABLE_DATA:
log.console("Enabling data for UE ID=%d\n", run_id);
ue->enable_data();
break;
case DISABLE_DATA:
log.console("Disabling data for UE ID=%d\n", run_id);
ue->disable_data();
break;
}
}
if (pcell_idx == -1) {
log.debug("Skipping TTI. Pcell not yet selected.\n");
continue; continue;
} }
// DL/UL processing if UE has selected cell int fd = events[i].data.fd;
dl_rnti = ue->get_dl_sched_rnti(tti); if (event_handler.find(fd) != event_handler.end()) {
if (SRSLTE_RNTI_ISSI(dl_rnti)) { event_handler[fd]->handle_event(fd, events[i], epoll_fd);
// deliver SIBs one after another
mac_interface_phy_lte::mac_grant_dl_t dl_grant = {};
dl_grant.pid = get_pid(tti);
dl_grant.rnti = dl_rnti;
dl_grant.tb[0].tbs = cells[pcell_idx]->sibs[sib_idx]->N_bytes;
dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti);
ue->new_tb(dl_grant, cells[pcell_idx]->sibs[sib_idx]->msg);
log.info("Delivered SIB%d for pcell_idx=%d\n", sib_idx, pcell_idx);
sib_idx = (sib_idx + 1) % cells[pcell_idx]->sibs.size();
} else if (SRSLTE_RNTI_ISRAR(dl_rnti)) {
if (prach_tti != -1) {
rar_tti = (prach_tti + 3) % 10240;
if (tti == rar_tti) {
send_rar(prach_preamble_index);
}
}
} else if (SRSLTE_RNTI_ISPA(dl_rnti)) {
log.debug("Searching for paging RNTI\n");
// PCH will be triggered from SYSSIM after receiving Paging
} else if (SRSLTE_RNTI_ISUSER(dl_rnti)) {
// check if this is for contention resolution after PRACH/RAR
if (dl_rnti == crnti) {
log.debug("Searching for C-RNTI=%d\n", crnti);
if (rar_tti != -1) {
msg3_tti = (rar_tti + 3) % 10240;
if (tti == msg3_tti) {
send_msg3_grant();
rar_tti = -1;
}
}
}
// check for SR
if (sr_tti != -1) {
send_sr_ul_grant();
}
if (dl_rnti != 0) {
log.debug("Searching for RNTI=%d\n", dl_rnti);
// look for DL data to be send in each bearer and provide grant accordingly
for (int lcid = 0; lcid < SRSLTE_N_RADIO_BEARERS; lcid++) {
uint32_t buf_state = rlc.get_buffer_state(lcid);
if (buf_state > 0) {
log.debug("LCID=%d, buffer_state=%d\n", lcid, buf_state);
const uint32_t mac_header_size = 10; // Add MAC header (10 B for all subheaders, etc)
if (tmp_rlc_buffer.get_tailroom() > (buf_state + mac_header_size)) {
uint32_t pdu_size = rlc.read_pdu(lcid, tmp_rlc_buffer.msg, buf_state);
tx_payload_buffer.clear();
mac_msg_dl.init_tx(&tx_payload_buffer, pdu_size + mac_header_size, false);
// check if this is Msg4 that needs to contain the contention resolution ID CE
if (msg3_tti != -1) {
if (lcid == 0) {
if (mac_msg_dl.new_subh()) {
if (mac_msg_dl.get()->set_con_res_id(conres_id)) {
log.info("CE: Added Contention Resolution ID=0x%" PRIx64 "\n", conres_id);
} else {
log.error("CE: Setting Contention Resolution ID CE\n");
}
} else {
log.error("CE: Setting Contention Resolution ID CE. No space for a subheader\n");
}
msg3_tti = -1;
}
}
// Add payload
if (mac_msg_dl.new_subh()) {
int n = mac_msg_dl.get()->set_sdu(lcid, pdu_size, tmp_rlc_buffer.msg);
if (n == -1) {
log.error("Error while adding SDU (%d B) to MAC PDU\n", pdu_size);
mac_msg_dl.del_subh();
}
}
uint8_t* mac_pdu_ptr = mac_msg_dl.write_packet(&log);
if (mac_pdu_ptr != nullptr) {
log.info_hex(
mac_pdu_ptr, mac_msg_dl.get_pdu_len(), "DL MAC PDU (%d B):\n", mac_msg_dl.get_pdu_len());
// Prepare MAC grant for CCCH
mac_interface_phy_lte::mac_grant_dl_t dl_grant = {};
dl_grant.pid = get_pid(tti);
dl_grant.rnti = dl_rnti;
dl_grant.tb[0].tbs = mac_msg_dl.get_pdu_len();
dl_grant.tb[0].ndi_present = true;
dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti);
ue->new_tb(dl_grant, (const uint8_t*)mac_pdu_ptr);
} else {
log.error("Error writing DL MAC PDU\n");
}
mac_msg_dl.reset();
} else {
log.error("Can't fit RLC PDU into buffer (%d > %d)\n", buf_state, tmp_rlc_buffer.get_tailroom());
}
}
}
// Check if we need to provide a UL grant as well
}
} else {
log.debug("Not handling RNTI=%d\n", dl_rnti);
} }
} }
usleep(1000);
} }
return SRSLTE_SUCCESS;
log.info("Leaving main thread.\n");
log.console("Leaving main thread.\n");
} }
uint32_t get_tti() { return tti; } uint32_t get_tti() { return tti; }
@ -927,6 +981,13 @@ private:
ttcn3_ip_ctrl_interface ip_ctrl; ttcn3_ip_ctrl_interface ip_ctrl;
ttcn3_srb_interface srb; ttcn3_srb_interface srb;
// Epoll
int epoll_fd = -1;
int signal_fd = -1; ///< FD for signals
std::map<uint32_t, epoll_handler*> event_handler; ///< Lookup table for handler
epoll_timer_handler timer_handler;
epoll_signal_handler signal_handler;
// Logging stuff // Logging stuff
srslte::logger_stdout logger_stdout; srslte::logger_stdout logger_stdout;
srslte::logger_file* logger_file = nullptr; srslte::logger_file* logger_file = nullptr;
@ -946,9 +1007,8 @@ private:
srslte::byte_buffer_pool* pool = nullptr; srslte::byte_buffer_pool* pool = nullptr;
// Simulator vars // Simulator vars
unique_ptr<ttcn3_ue> ue = nullptr; ttcn3_ue* ue = nullptr;
std::mutex mutex; bool running = false;
bool running = false;
typedef enum { UE_SWITCH_ON = 0, UE_SWITCH_OFF, ENABLE_DATA, DISABLE_DATA } ss_events_t; typedef enum { UE_SWITCH_ON = 0, UE_SWITCH_OFF, ENABLE_DATA, DISABLE_DATA } ss_events_t;
block_queue<ss_events_t> event_queue; block_queue<ss_events_t> event_queue;
@ -975,6 +1035,7 @@ private:
float attenuation = 0.0; float attenuation = 0.0;
uint32_t earfcn = 0; uint32_t earfcn = 0;
std::vector<unique_byte_buffer_t> sibs; std::vector<unique_byte_buffer_t> sibs;
int sib_idx = 0; ///< Index of SIB scheduled for next transmission
} syssim_cell_t; } syssim_cell_t;
typedef std::unique_ptr<syssim_cell_t> unique_syssim_cell_t; typedef std::unique_ptr<syssim_cell_t> unique_syssim_cell_t;
std::vector<unique_syssim_cell_t> cells; std::vector<unique_syssim_cell_t> cells;
@ -1004,6 +1065,14 @@ private:
std::vector<std::string> rb_id_vec = std::vector<std::string> rb_id_vec =
{"SRB0", "SRB1", "SRB2", "DRB1", "DRB2", "DRB3", "DRB4", "DRB5", "DRB6", "DRB7", "DRB8"}; {"SRB0", "SRB1", "SRB2", "DRB1", "DRB2", "DRB3", "DRB4", "DRB5", "DRB6", "DRB7", "DRB8"};
// port constants
const std::string listen_address = "0.0.0.0";
const uint32_t UT_PORT = 2222;
const uint32_t SYS_PORT = 2223;
const uint32_t IPSOCK_PORT = 2224;
const uint32_t IPCTRL_PORT = 2225;
const uint32_t SRB_PORT = 2226;
}; };
#endif // SRSUE_TTCN3_SYSSIM_H #endif // SRSUE_TTCN3_SYSSIM_H

View File

@ -31,119 +31,88 @@
using namespace rapidjson; using namespace rapidjson;
// The UpperTester interface // The UpperTester interface
class ttcn3_ut_interface : public netsource_handler class ttcn3_ut_interface : public ttcn3_port_handler
{ {
public: public:
ttcn3_ut_interface() : netsource_handler("TTCN3_UT_IF") {} ttcn3_ut_interface() {}
~ttcn3_ut_interface(){}; ~ttcn3_ut_interface(){};
void init(ss_ut_interface* syssim_, srslte::log* log_, std::string net_ip_, uint32_t net_port_) int init(ss_ut_interface* syssim_, srslte::log* log_, std::string net_ip_, uint32_t net_port_)
{ {
syssim = syssim_; syssim = syssim_;
log = log_; log = log_;
net_ip = net_ip_; net_ip = net_ip_;
net_port = net_port_; net_port = net_port_;
initialized = true; initialized = true;
log->debug("Initialized.\n");
// create socket and
return port_listen();
} }
private: private:
void run_thread() int handle_message(const unique_byte_array_t& rx_buf, const uint32_t n)
{ {
if (!initialized) { Document document;
fprintf(stderr, "UT interface not initialized. Exiting.\n"); if (document.Parse((char*)rx_buf->begin()).HasParseError() || document.IsObject() == false) {
exit(-1); log->error_hex(rx_buf->begin(), n, "Error parsing incoming data.\n");
return SRSLTE_ERROR;
} }
// open TCP socket // Pretty-print
if (srslte_netsource_init(&net_source, net_ip.c_str(), net_port, SRSLTE_NETSOURCE_TCP)) { StringBuffer buffer;
fprintf(stderr, "Error creating input TCP socket at port %d\n", net_port); PrettyWriter<StringBuffer> writer(buffer);
exit(-1); document.Accept(writer);
} log->info("Received %d bytes\n%s\n", n, (char*)buffer.GetString());
log->info("Listening on %s:%d for incoming connections ..\n", net_ip.c_str(), net_port); // check for command
assert(document.HasMember("Cmd"));
assert(document["Cmd"].IsObject());
running = true; // get Cmd
const Value& a = document["Cmd"];
int n; if (a.HasMember("MMI")) {
while (run_enable) { assert(a.HasMember("MMI"));
log->debug("Reading from UT port ..\n");
n = srslte_netsource_read(&net_source, rx_buf->begin(), RX_BUF_SIZE); // get MMI and make sure it has another Cmd nested
if (n > 0) { const Value& mmi = a["MMI"];
// Terminate assert(mmi.HasMember("Cmd"));
rx_buf->at(n) = '\0';
Document document; // get MMI cmd
if (document.Parse((char*)rx_buf->begin()).HasParseError()) { const Value& mmi_cmd = mmi["Cmd"];
log->error_hex(rx_buf->begin(), n, "Error parsing incoming data.\n"); assert(mmi_cmd.IsString());
continue;
}
assert(document.IsObject());
// Pretty-print // check for CnfRequired
StringBuffer buffer; assert(document.HasMember("CnfRequired"));
PrettyWriter<StringBuffer> writer(buffer);
document.Accept(writer);
log->info("Received %d bytes\n%s\n", n, (char*)buffer.GetString());
// check for command if (strcmp(mmi_cmd.GetString(), "POWER_OFF") == 0) {
assert(document.HasMember("Cmd")); log->info("Received POWER_OFF command.\n");
assert(document["Cmd"].IsObject()); handle_power_off(document);
} else if (strcmp(mmi_cmd.GetString(), "SWITCH_ON") == 0) {
// get Cmd log->info("Received SWITCH_ON command.\n");
const Value& a = document["Cmd"]; syssim->switch_on_ue();
} else if (strcmp(mmi_cmd.GetString(), "SWITCH_OFF") == 0) {
if (a.HasMember("MMI")) { log->info("Received SWITCH_OFF command.\n");
assert(a.HasMember("MMI")); syssim->switch_off_ue();
// get MMI and make sure it has another Cmd nested
const Value& mmi = a["MMI"];
assert(mmi.HasMember("Cmd"));
// get MMI cmd
const Value& mmi_cmd = mmi["Cmd"];
assert(mmi_cmd.IsString());
// check for CnfRequired
assert(document.HasMember("CnfRequired"));
if (strcmp(mmi_cmd.GetString(), "POWER_OFF") == 0) {
log->info("Received POWER_OFF command.\n");
handle_power_off(document);
} else if (strcmp(mmi_cmd.GetString(), "SWITCH_ON") == 0) {
log->info("Received SWITCH_ON command.\n");
syssim->switch_on_ue();
} else if (strcmp(mmi_cmd.GetString(), "SWITCH_OFF") == 0) {
log->info("Received SWITCH_OFF command.\n");
syssim->switch_off_ue();
} else {
log->error("Received unknown command: %s\n", mmi_cmd.GetString());
}
} else if (a.HasMember("AT")) {
handle_at_command(document);
} else if (a.HasMember("TC_START")) {
log->info("Received TC_START command.\n");
const Value& cmd = a["TC_START"];
assert(cmd.HasMember("Name"));
const Value& tc_name = cmd["Name"];
syssim->tc_start(tc_name.GetString());
} else if (a.HasMember("TC_END")) {
log->info("Received TC_END command.\n");
syssim->tc_end();
} else {
log->error("Unknown command type.\n");
}
} else if (n == 0) {
log->error("Connection closed on UT interface.\n");
} else { } else {
log->error("Error receiving from network\n"); log->error("Received unknown command: %s\n", mmi_cmd.GetString());
exit(-1);
} }
} else if (a.HasMember("AT")) {
handle_at_command(document);
} else if (a.HasMember("TC_START")) {
log->info("Received TC_START command.\n");
const Value& cmd = a["TC_START"];
assert(cmd.HasMember("Name"));
const Value& tc_name = cmd["Name"];
syssim->tc_start(tc_name.GetString());
} else if (a.HasMember("TC_END")) {
log->info("Received TC_END command.\n");
syssim->tc_end();
} else {
log->error("Unknown command type.\n");
} }
running = false;
srslte_netsource_free(&net_source); return SRSLTE_SUCCESS;
} }
void handle_power_off(Document& document) void handle_power_off(Document& document)
@ -161,9 +130,7 @@ private:
resp.Accept(writer); resp.Accept(writer);
log->info("Sending %s to tester (%zd B)\n", buffer.GetString(), buffer.GetSize()); log->info("Sending %s to tester (%zd B)\n", buffer.GetString(), buffer.GetSize());
if (srslte_netsource_write(&net_source, (char*)buffer.GetString(), buffer.GetSize()) != SRSLTE_SUCCESS) { send((const uint8_t*)buffer.GetString(), buffer.GetSize());
log->error("Error sending message to tester.\n");
}
} }
void handle_at_command(Document& document) void handle_at_command(Document& document)

View File

@ -30,5 +30,6 @@ target_link_libraries(ttcn3_dut srsue_stack
srsue_phy srsue_phy
srsue_mac srsue_mac
rrc_asn1 rrc_asn1
sctp
${Boost_LIBRARIES}) ${Boost_LIBRARIES})
include_directories(${PROJECT_SOURCE_DIR}/srsue/test/ttcn3/hdr) include_directories(${PROJECT_SOURCE_DIR}/srsue/test/ttcn3/hdr)

View File

@ -28,8 +28,6 @@ namespace srsue {
lte_ttcn3_phy::lte_ttcn3_phy(srslte::logger* logger_) : logger(logger_) {} lte_ttcn3_phy::lte_ttcn3_phy(srslte::logger* logger_) : logger(logger_) {}
lte_ttcn3_phy::~lte_ttcn3_phy() {}
int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, syssim_interface_phy* syssim_) int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, syssim_interface_phy* syssim_)
{ {
stack = stack_; stack = stack_;

View File

@ -19,8 +19,6 @@
* *
*/ */
#include "rapidjson/document.h" // rapidjson's DOM-style API
#include "rapidjson/prettywriter.h" // for stringify JSON
#include "srslte/build_info.h" #include "srslte/build_info.h"
#include "srslte/common/logmap.h" #include "srslte/common/logmap.h"
#include "srsue/hdr/ue.h" #include "srsue/hdr/ue.h"
@ -28,7 +26,6 @@
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/program_options/parsers.hpp> #include <boost/program_options/parsers.hpp>
#include <iostream> #include <iostream>
#include <signal.h>
using namespace srslte; using namespace srslte;
using namespace srsue; using namespace srsue;
@ -112,39 +109,26 @@ all_args_t parse_args(ttcn3_dut_args_t* args, int argc, char* argv[])
return all_args; return all_args;
} }
bool go_exit = false;
void sig_int_handler(int signo)
{
printf("SIGINT received. Exiting...\n");
if (signo == SIGINT) {
go_exit = true;
}
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
std::cout << "Built in " << srslte_get_build_mode() << " mode using " << srslte_get_build_info() << "." << std::endl; std::cout << "Built in " << srslte_get_build_mode() << " mode using " << srslte_get_build_info() << "." << std::endl;
ttcn3_dut_args_t dut_args; ttcn3_dut_args_t dut_args = {};
all_args_t ue_args = parse_args(&dut_args, argc, argv); all_args_t ue_args = parse_args(&dut_args, argc, argv);
signal(SIGINT, sig_int_handler);
// Instantiate file logger // Instantiate file logger
srslte::logger_file logger_file; srslte::logger_file logger_file;
srslte::logmap::get_instance()->set_default_logger(&logger_file); srslte::logmap::get_instance()->set_default_logger(&logger_file);
// create and init SYSSIM // Create UE object
ttcn3_syssim syssim(&logger_file); unique_ptr<ttcn3_ue> ue = std::unique_ptr<ttcn3_ue>(new ttcn3_ue());
syssim.init(ue_args);
// Loop until finished .. // create and init SYSSIM
while (!go_exit) { ttcn3_syssim syssim(&logger_file, ue.get());
sleep(1); if (syssim.init(ue_args) != SRSLTE_SUCCESS) {
fprintf(stderr, "Error: Couldn't initialize system simulator\n");
return SRSLTE_ERROR;
} }
syssim.stop(); return syssim.run();
return SRSLTE_SUCCESS;
} }

View File

@ -24,5 +24,5 @@ add_executable(rapidjson_test rapidjson_test.cc)
add_test(rapidjson_test rapidjson_test) add_test(rapidjson_test rapidjson_test)
add_executable(ttcn3_if_handler_test ttcn3_if_handler_test.cc) add_executable(ttcn3_if_handler_test ttcn3_if_handler_test.cc)
target_link_libraries(ttcn3_if_handler_test srslte_phy srslte_common) target_link_libraries(ttcn3_if_handler_test sctp srslte_phy srslte_common)
add_test(ttcn3_if_handler_test ttcn3_if_handler_test) add_test(ttcn3_if_handler_test ttcn3_if_handler_test)

View File

@ -19,15 +19,11 @@
* *
*/ */
#include "srslte/srslte.h"
#include "ttcn3_ip_ctrl_interface.h" #include "ttcn3_ip_ctrl_interface.h"
#include "ttcn3_ip_sock_interface.h" #include "ttcn3_ip_sock_interface.h"
#include "ttcn3_srb_interface.h" #include "ttcn3_srb_interface.h"
#include "ttcn3_sys_interface.h" #include "ttcn3_sys_interface.h"
#include "ttcn3_ut_interface.h" #include "ttcn3_ut_interface.h"
#include <iostream>
#include <stdio.h>
#include <vector>
int if_handler_test() int if_handler_test()
{ {