Virtual Testbed
Ship dynamics simulator for extreme conditions
server.hh
1 #ifndef VTESTBED_SERVER_SERVER_HH
2 #define VTESTBED_SERVER_SERVER_HH
3 
4 #include <chrono>
5 #include <mutex>
6 #include <unordered_map>
7 
8 #include <unistdx/base/log_message>
9 #include <unistdx/io/poller>
10 #include <unistdx/net/socket>
11 
12 #include <vtestbed/core/testbed.hh>
13 #include <vtestbed/server/macros.hh>
14 #include <vtestbed/server/remote_client.hh>
15 
16 namespace vtb {
17 
19  namespace srv {
20 
21  template <class T>
22  class Server {
23 
24  private:
28  typedef clock_type::duration duration;
29  typedef clock_type::time_point time_point;
31 
32  private:
33  sys::socket_address _address;
34  sys::socket _socket;
35  sys::event_poller _poller;
37  testbed_type _testbed;
38  std::mutex _mutex;
39  bool _verbose = false;
41  duration _timeout = std::chrono::seconds(7);
43  duration _period = std::chrono::seconds(1);
45  time_point _nextpush = time_point(duration::zero());
47  time_point _lastpush = time_point(duration::zero());
48 
49  public:
50 
51  inline explicit
52  Server(const sys::socket_address& address):
53  _address(address) {}
54 
55  void
56  run() {
57  listen();
58  _nextpush = clock_type::now();
59  _lastpush = _nextpush;
60  while (true) {
61  _poller.wait_until(_mutex, _nextpush);
62  time_point now = clock_type::now();
63  if (_nextpush <= now) {
64  push_updates(now);
65  _nextpush = now + _period;
66  }
67  for (const sys::epoll_event& ev : _poller) {
68  if (ev.fd() == _socket.fd()) {
69  accept_connection();
70  } else {
71  handle_client(ev);
72  }
73  }
74  for (auto& pair : _clients) {
75  auto& client = pair.second;
76  client.handshake();
77  client.flush();
78  }
79  }
80  }
81 
82  inline void
83  verbose(bool rhs) {
84  _verbose = rhs;
85  }
86 
87  void
88  listen() {
89  this->info("add server _", _address);
90  _socket.bind(_address);
91  #if defined(UNISTDX_HAVE_TCP_USER_TIMEOUT)
92  VTB_WARN(_socket.set_user_timeout(_timeout));
93  #endif
94  _socket.listen();
95  _poller.emplace(_socket.fd(), sys::event::in);
96  }
97 
98  void
99  accept_connection() {
100  sys::socket sock;
101  sys::socket_address addr;
102  try {
103  _socket.accept(sock, addr);
104  } catch (const sys::bad_call& err) {
105  if (err.errc() == std::errc::resource_unavailable_try_again) {
106  return;
107  }
108  throw;
109  }
110  this->info("add client _", addr);
111  #if defined(UNISTDX_HAVE_TCP_USER_TIMEOUT)
112  VTB_WARN(sock.set_user_timeout(_timeout));
113  #endif
114  sys::fd_type fd = sock.fd();
115  _poller.emplace(fd, sys::event::in);
116  client_type client{std::move(sock), addr, true};
117  client.verbose(_verbose);
118  _clients.emplace(fd, std::move(client));
119  }
120 
121  void
122  handle_client(const sys::epoll_event& ev) {
123  auto result = _clients.find(ev.fd());
124  if (result == _clients.end()) {
125  this->error("unknown client, ev _", ev);
126  for (auto& pair : _clients) {
127  this->error(
128  "client _, fd _",
129  pair.second.address(),
130  pair.first
131  );
132  }
133  return;
134  }
135  auto& client = result->second;
136  client.process(ev, _testbed);
137  if (ev.hup()) {
138  this->info("remove client _", client.address());
139  _poller.erase(ev);
140  _clients.erase(result);
141  }
142  }
143 
144  void
145  push_updates(time_point now) {
146  float_duration delta = now - _lastpush;
147  if (!(delta < float_duration::zero())) {
148  const T dt = delta.count();
149  _testbed.reset();
150  //_testbed.ship_motion_solver()->min_step(dt);
151  //_testbed.ship_motion_solver()->verbose(false);
152  _testbed.step(dt);
153  }
154  for (auto& pair : _clients) {
155  auto& client = pair.second;
156  client.push(_testbed);
157  }
158  _lastpush = now;
159  }
160 
161  private:
162 
163  template <class ... Args>
164  inline void
165  info(const Args& ... args) {
166  if (_verbose) {
167  sys::log_message("server", args ...);
168  }
169  }
170 
171  template <class ... Args>
172  inline void
173  error(const Args& ... args) {
174  sys::log_message("server", args ...);
175  }
176 
177  };
178 
179  }
180 
181 }
182 
183 #endif // vim:filetype=cpp
Main namespace.
Definition: convert.hh:9
void reset()
Reset simulation to the initial state and retain solver configuration.