source: finroc_plugins_structure/main_utilities.cpp @ 146:a4bae722af30

Last change on this file since 146:a4bae722af30 was 146:a4bae722af30, checked in by Max Reichardt <max.reichardt@…>, 16 months ago

Merge with 17.03

File size: 17.0 KB
Line 
1//
2// You received this file as part of Finroc
3// A framework for intelligent robot control
4//
5// Copyright (C) Finroc GbR (finroc.org)
6//
7// This program is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License along
18// with this program; if not, write to the Free Software Foundation, Inc.,
19// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20//
21//----------------------------------------------------------------------
22/*!\file    plugins/structure/main_utilities.cpp
23 *
24 * \author  Tobias Foehst
25 * \author  Max Reichardt
26 * \author  Michael Arndt
27 *
28 * \date    2013-05-16
29 *
30 */
31//----------------------------------------------------------------------
32#include "plugins/structure/main_utilities.h"
33
34//----------------------------------------------------------------------
35// External includes (system with <>, local with "")
36//----------------------------------------------------------------------
37#include <cstdlib>
38#include <csignal>
39#include <mutex>
40#include <condition_variable>
41
42extern "C"
43{
44#include <libgen.h>
45}
46
47#include "rrlib/logging/configuration.h"
48#include "rrlib/logging/sinks/tNonBlockingSink.h"
49#ifdef _LIB_RRLIB_CRASH_HANDLER_PRESENT_
50#include "rrlib/crash_handler/crash_handler.h"
51#endif
52
53#include "core/file_lookup.h"
54#include "core/tRuntimeEnvironment.h"
55#include "plugins/parameters/tConfigFile.h"
56#include "plugins/parameters/tConfigurablePlugin.h"
57#include "plugins/scheduling/tExecutionControl.h"
58#include "plugins/scheduling/scheduling.h"
59#include "plugins/scheduling/tThreadContainerThread.h"
60
61#ifdef _LIB_FINROC_PLUGINS_NETWORK_TRANSPORT_GENERIC_PROTOCOL_PRESENT_
62#include "plugins/network_transport/generic_protocol/tLocalRuntimeInfo.h"
63#endif
64#ifdef _LIB_FINROC_PLUGINS_TCP_PRESENT_
65#include "plugins/tcp/tTCPPlugin.h"
66#endif
67
68#include "rrlib/getopt/parser.h"
69
70//----------------------------------------------------------------------
71// Internal includes with ""
72//----------------------------------------------------------------------
73#include "plugins/structure/tComponent.h"
74
75extern bool make_all_port_links_unique;
76
77//----------------------------------------------------------------------
78// Debugging
79//----------------------------------------------------------------------
80#include <cassert>
81
82//----------------------------------------------------------------------
83// Namespace usage
84//----------------------------------------------------------------------
85
86//----------------------------------------------------------------------
87// Namespace declaration
88//----------------------------------------------------------------------
89namespace finroc
90{
91namespace structure
92{
93
94//----------------------------------------------------------------------
95// Forward declarations / typedefs / enums
96//----------------------------------------------------------------------
97
98//----------------------------------------------------------------------
99// Const values
100//----------------------------------------------------------------------
101
102//----------------------------------------------------------------------
103// Implementation
104//----------------------------------------------------------------------
105
106namespace
107{
108
109bool run_main_loop = false;
110bool pause_at_startup = false;
111#ifdef NDEBUG
112bool enable_crash_handler = false;
113#else
114bool enable_crash_handler = true;
115#endif
116rrlib::time::tTimeMode time_mode = rrlib::time::tTimeMode::STEADY_INITIAL_SYSTEM_TIME;
117
118// We do not use stuff from rrlib_thread, because we have the rare case that in signal handler
119// waiting thread does something else, which is problematic with respect to enforcing lock order
120std::mutex main_thread_wait_mutex;
121#ifndef RRLIB_SINGLE_THREADED
122std::condition_variable main_thread_wait_variable;
123#endif
124
125/*!
126 * Shuts down program cleanly by stopping all threads and deleting all framework elements.
127 *
128 * \return True if function is called for the first time
129 */
130bool Shutdown(const char* signal_name)
131{
132  static int call_count = 0; // How many time has function been called?
133  call_count++;
134  if (call_count == 1)
135  {
136    FINROC_LOG_PRINT_STATIC(USER, "\nCaught ", signal_name, ". Exiting...");
137    run_main_loop = false;
138    std::unique_lock<std::mutex> l(main_thread_wait_mutex);
139#ifndef RRLIB_SINGLE_THREADED
140    main_thread_wait_variable.notify_all();
141#endif
142
143    if (definitions::cSINGLE_THREADED && scheduling::tThreadContainerThread::CurrentThread())
144    {
145      scheduling::tThreadContainerThread::CurrentThread()->StopThread();
146    }
147    return true;
148  }
149  return false;
150}
151
152//----------------------------------------------------------------------
153// HandleSignalSIGINT
154//----------------------------------------------------------------------
155void HandleSignalSIGINT(int signal)
156{
157  static int call_count = 0; // How many time has function been called?
158  assert(signal == SIGINT);
159  call_count++;
160  if ((!Shutdown("SIGINT")) && call_count < 5)
161  {
162    FINROC_LOG_PRINT_STATIC(USER, "\nCaught SIGINT again. Unfortunately, the program still has not terminated. Program will be aborted at fifth SIGINT.");
163  }
164  else if (call_count >= 5)
165  {
166    FINROC_LOG_PRINT_STATIC(USER, "\nCaught SIGINT for the fifth time. Aborting program.");
167    abort();
168  }
169}
170
171//----------------------------------------------------------------------
172// HandleSignalSIGINT
173//----------------------------------------------------------------------
174void HandleSignalSIGTERM(int signal)
175{
176  assert(signal == SIGTERM);
177  if (!Shutdown("SIGTERM"))
178  {
179    FINROC_LOG_PRINT_STATIC(USER, "\nCaught SIGTERM while shutting down. Aborting program.");
180    abort();
181  }
182}
183
184//----------------------------------------------------------------------
185// OptionsHandler
186//----------------------------------------------------------------------
187bool OptionsHandler(const rrlib::getopt::tNameToOptionMap &name_to_option_map)
188{
189  // log-config
190  rrlib::getopt::tOption log_config(name_to_option_map.at("log-config"));
191  if (log_config->IsActive())
192  {
193    rrlib::getopt::tOption offline(name_to_option_map.at("offline"));
194    bool set_offline = offline->IsActive();
195    rrlib::logging::sinks::tNonBlockingSink::RegisterAtFactory();
196    rrlib::logging::ConfigureFromFile(rrlib::getopt::EvaluateValue(log_config), set_offline);
197  }
198
199  // config-file
200  rrlib::getopt::tOption parameter_config(name_to_option_map.at("config-file"));
201  if (parameter_config->IsActive())
202  {
203    parameters::tConfigFile* config_file = nullptr;
204    for (auto & file : rrlib::getopt::EvaluateValueList(parameter_config))
205    {
206      if (!core::FinrocFileExists(file))
207      {
208        FINROC_LOG_PRINT_STATIC(ERROR, "Could not find specified config file ", file);
209        return false;
210      }
211      else
212      {
213        FINROC_LOG_PRINT_STATIC(DEBUG, "Loading config file ", file);
214      }
215      if (!config_file)
216      {
217        parameters::tConfigurablePlugin::SetConfigFile(file);
218        config_file = new parameters::tConfigFile(file);
219        core::tRuntimeEnvironment::GetInstance().AddAnnotation(*config_file);
220      }
221      else
222      {
223        try
224        {
225          config_file->Append(file);
226        }
227        catch (const std::exception& e)
228        {
229          FINROC_LOG_PRINT(ERROR, "Could not append config file '", file, "': ", e.what());
230        }
231      }
232    }
233  }
234
235  // pause
236  rrlib::getopt::tOption pause(name_to_option_map.at("pause"));
237  pause_at_startup = pause->IsActive();
238
239  // port
240  rrlib::getopt::tOption port_option(name_to_option_map.at("port"));
241  if (port_option->IsActive())
242  {
243#ifdef _LIB_FINROC_PLUGINS_TCP_PRESENT_
244    int port = atoi(rrlib::getopt::EvaluateValue(port_option).c_str());
245    if (port < 1 || port > 65535)
246    {
247      FINROC_LOG_PRINT_STATIC(ERROR, "Invalid port '", port, "'. Using default: ", tcp::tTCPPlugin::GetInstance().par_preferred_server_port.Get());
248    }
249    else
250    {
251      FINROC_LOG_PRINT_STATIC(DEBUG, "Listening on user defined port ", port, ".");
252      tcp::tTCPPlugin::GetInstance().par_preferred_server_port.Set(port);
253    }
254#endif
255  }
256
257  // port-links-are-not-unique
258  rrlib::getopt::tOption opt(name_to_option_map.at("port-links-are-not-unique"));
259  if (opt->IsActive())
260  {
261    make_all_port_links_unique = false;
262  }
263
264  // connect
265  rrlib::getopt::tOption connect_option(name_to_option_map.at("connect"));
266  if (connect_option->IsActive())
267  {
268#ifdef _LIB_FINROC_PLUGINS_TCP_PRESENT_
269    for (auto & address : rrlib::getopt::EvaluateValueList(connect_option))
270    {
271      tcp::tTCPPlugin::GetInstance().AddRuntimeToConnectTo(address);
272      FINROC_LOG_PRINT_STATIC(DEBUG, "Connecting to ", address);
273    }
274#endif
275  }
276
277  // listen-address
278  rrlib::getopt::tOption listen_address_option(name_to_option_map.at("listen-address"));
279  if (listen_address_option->IsActive())
280  {
281#ifdef _LIB_FINROC_PLUGINS_TCP_PRESENT_
282    tcp::tTCPPlugin::GetInstance().par_server_listen_address.Set(rrlib::getopt::EvaluateValue(listen_address_option));
283    FINROC_LOG_PRINT_STATIC(DEBUG, "Listening on ", tcp::tTCPPlugin::GetInstance().par_server_listen_address.Get());
284#endif
285  }
286
287  // crash-handler
288  rrlib::getopt::tOption crash_config(name_to_option_map.at("crash-handler"));
289  if (crash_config->IsActive())
290  {
291    std::string s(rrlib::getopt::EvaluateValue(crash_config));
292    if (s.compare("on") == 0)
293    {
294      enable_crash_handler = true;
295    }
296    else if (s.compare("off") == 0)
297    {
298      enable_crash_handler = false;
299    }
300    else
301    {
302      FINROC_LOG_PRINT_STATIC(ERROR, "Option --crash-handler needs be either 'on' or 'off' (not '", s, "').");
303      return false;
304    }
305  }
306
307  // profiling
308  rrlib::getopt::tOption profiling(name_to_option_map.at("profiling"));
309  if (profiling->IsActive())
310  {
311    scheduling::SetProfilingEnabled(true);
312  }
313
314  // component visualization
315  rrlib::getopt::tOption disable_component_visualization(name_to_option_map.at("disable-component-visualization"));
316  if (disable_component_visualization->IsActive())
317  {
318    tComponent::SetComponentVisualizationEnabled(false);
319  }
320
321  // default clock
322  rrlib::getopt::tOption default_clock_option(name_to_option_map.at("default-clock"));
323  if (default_clock_option->IsActive())
324  {
325    std::string mode = rrlib::getopt::EvaluateValue(default_clock_option);
326    if (mode == "system")
327    {
328      time_mode = rrlib::time::tTimeMode::SYSTEM_TIME;
329    }
330    else if (mode == "steady-initial-system-time")
331    {
332      time_mode = rrlib::time::tTimeMode::STEADY_INITIAL_SYSTEM_TIME;
333    }
334    else
335    {
336      FINROC_LOG_PRINT_STATIC(ERROR, "Option --default-clock needs to be either 'system' or 'steady-initial-system-time'");
337      return false;
338    }
339  }
340  return true;
341}
342
343}
344
345//----------------------------------------------------------------------
346// InstallSignalHandler
347//----------------------------------------------------------------------
348bool InstallSignalHandler()
349{
350#if __linux__
351  struct sigaction signal_action;
352  signal_action.sa_handler = HandleSignalSIGINT;
353  sigemptyset(&signal_action.sa_mask);
354  signal_action.sa_flags = 0;
355
356  if (sigaction(SIGINT, &signal_action, NULL) != 0)
357  {
358    perror("Could not install signal handler");
359    return false;
360  }
361
362  signal_action.sa_handler = HandleSignalSIGTERM;
363  if (sigaction(SIGTERM, &signal_action, NULL) != 0)
364  {
365    perror("Could not install signal handler");
366    return false;
367  }
368
369#else
370  signal(SIGINT, HandleSignalSIGINT);
371  signal(SIGTERM, HandleSignalSIGTERM);
372#endif
373
374  return true;
375}
376
377//----------------------------------------------------------------------
378// RegisterCommonOptions
379//----------------------------------------------------------------------
380void RegisterCommonOptions()
381{
382  rrlib::getopt::AddValue("log-config", 'l', "Log config file", &OptionsHandler, true);
383  rrlib::getopt::AddValue("config-file", 'c', "Parameter config file", &OptionsHandler);
384  rrlib::getopt::AddValue("listen-address", 0, "Address on which to listen for connections (default: 0.0.0.0), set this to :: to enable IPv6", &OptionsHandler);
385  rrlib::getopt::AddValue("port", 'p', "Network port to use", &OptionsHandler, true);
386  rrlib::getopt::AddValue("connect", 0, "TCP address of finroc application to connect to (default: localhost:<port>)", &OptionsHandler);
387  rrlib::getopt::AddValue("crash-handler", 0, "Enable/disable crash handler (default: 'on' in debug mode - 'off' in release mode).", &OptionsHandler, true);
388  rrlib::getopt::AddFlag("pause", 0, "Pause program at startup", &OptionsHandler);
389  rrlib::getopt::AddFlag("port-links-are-not-unique", 0, "Port links in this part are not unique in P2P network (=> host name is prepended in GUI, for instance).", &OptionsHandler);
390  rrlib::getopt::AddFlag("profiling", 0, "Enables profiling (creates additional ports with profiling information)", &OptionsHandler);
391  rrlib::getopt::AddFlag("disable-component-visualization", 0, "Disables component visualization (no dedicated visualization ports will be created)", &OptionsHandler);
392  rrlib::getopt::AddFlag("offline", 0, "Disables online services which would retard software startup time.", &OptionsHandler);
393  rrlib::getopt::AddValue("default-clock", 0, "Default clock used for processing timestamps and durations ('system' and 'steady-initial-system-time' (default))", &OptionsHandler);
394}
395
396//----------------------------------------------------------------------
397// InstallCrashHandler
398//----------------------------------------------------------------------
399void InstallCrashHandler()
400{
401#ifdef _LIB_RRLIB_CRASH_HANDLER_PRESENT_
402  if (enable_crash_handler)
403  {
404    if (!rrlib::crash_handler::InstallCrashHandler())
405    {
406      FINROC_LOG_PRINT_STATIC(ERROR, "Error installing crash handler. Crashes will simply terminate the program.");
407    }
408  }
409#endif
410}
411
412//----------------------------------------------------------------------
413// ConnectTCPPeer
414//----------------------------------------------------------------------
415void SetRuntimeName(const std::string &name)
416{
417#ifdef _LIB_FINROC_PLUGINS_NETWORK_TRANSPORT_GENERIC_PROTOCOL_PRESENT_
418  network_transport::generic_protocol::tLocalRuntimeInfo::SetName(name);
419#endif
420}
421
422//----------------------------------------------------------------------
423// InitializeAndRunMainLoop
424//----------------------------------------------------------------------
425int InitializeAndRunMainLoop(const std::string &program_name)
426{
427  {
428    auto current_time_mode = rrlib::time::GetTimeMode();
429    if (current_time_mode == rrlib::time::tTimeMode::SYSTEM_TIME && current_time_mode != time_mode)
430    {
431      rrlib::time::SetTimeMode(time_mode);
432    }
433  }
434
435  core::tRuntimeEnvironment &runtime_environment = core::tRuntimeEnvironment::GetInstance();
436
437  typedef core::tFrameworkElement::tFlag tFlag;
438
439  // Have any top-level framework elements containing threads already been created?
440  // In this case, we won't create an extra thread container (finstructed part does not need one for example)
441  std::vector<core::tFrameworkElement*> executables;
442  for (auto it = runtime_environment.ChildrenBegin(); it != runtime_environment.ChildrenEnd(); ++it)
443  {
444    if (it->GetAnnotation<scheduling::tExecutionControl>() && (it->GetFlag(tFlag::FINSTRUCTABLE_GROUP) || it->GetFlag(tFlag::EDGE_AGGREGATOR)))
445    {
446      executables.push_back(&(*it));
447    }
448  }
449
450  for (size_t i = 0; i < executables.size(); i++)
451  {
452    core::tFrameworkElement* fe = executables[i];
453    if (!fe->IsReady())
454    {
455      fe->Init();
456    }
457  }
458
459#ifdef _LIB_FINROC_PLUGINS_NETWORK_TRANSPORT_GENERIC_PROTOCOL_PRESENT_
460  network_transport::generic_protocol::tLocalRuntimeInfo::StartServingStructure();
461#endif
462
463  for (size_t i = 0; i < executables.size(); i++)
464  {
465    core::tFrameworkElement* fe = executables[i];
466    if (pause_at_startup)
467    {
468      scheduling::tExecutionControl::PauseAll(*fe); // Shouldn't be necessary, but who knows what people might implement
469    }
470    else
471    {
472      scheduling::tExecutionControl::StartAll(*fe);
473      FINROC_LOG_PRINT_STATIC(USER, "Finroc program '", program_name, "' is now running.");
474    }
475  }
476
477  run_main_loop = true;
478  {
479    if (definitions::cSINGLE_THREADED && scheduling::tThreadContainerThread::CurrentThread())
480    {
481      scheduling::tThreadContainerThread::CurrentThread()->Run();
482    }
483    else
484    {
485      std::unique_lock<std::mutex> l(main_thread_wait_mutex);
486      while (run_main_loop)
487      {
488#ifndef RRLIB_SINGLE_THREADED
489        main_thread_wait_variable.wait_for(l, std::chrono::seconds(10));
490#else
491        std::this_thread::sleep_for(std::chrono::seconds(1));
492#endif
493      }
494    }
495  }
496  FINROC_LOG_PRINT_STATIC(DEBUG, "Left main loop");
497
498  // In many cases this is not necessary.
499  // However, doing this before static deinitialization can avoid issues with external libraries and thread container threads still running.
500  core::tRuntimeEnvironment::Shutdown();
501
502  return EXIT_SUCCESS;
503}
504
505//----------------------------------------------------------------------
506// End of declaration
507//----------------------------------------------------------------------
508}
509}
Note: See TracBrowser for help on using the repository browser.