C++ main entry point: Difference between revisions

From Rsewiki
 
(11 intermediate revisions by the same user not shown)
Line 1: Line 1:
Back to [[Robobot B]].
Back to [[Robobot B]].
Back to [[Robobot software description]]
Back to [[Robobot software description]]


== Main ==
== Main ==


C++ starts executing a function called ''main(int argc, char ** argv)''
C++ starts executing a function called ''main(int argc, char ** argv)''.
 
In Robobot, this is relatively simple, and the intention is explained here.
In Robobot, this is rather simple and the intention is explained here.


The main.cpp file looks like this (slightly reduced):
The main.cpp file looks like this (slightly reduced):
Line 23: Line 23:
  #include <string>
  #include <string>
  //
  //
  // include local files for data values and functions
  // Include local files for data values and functions
  #include "uservice.h"
  #include "uservice.h"
  #include "cmixer.h"
  #include "cmixer.h"
Line 36: Line 36:
   { // turn on LED on port 16
   { // turn on LED on port 16
     gpio.setPin(16, 1);
     gpio.setPin(16, 1);
     // run the planned missions
     //
    if (ini["plan20"]["run"] != "false")
     // Run the behaviour plans in this sequence
     { // example odometry drive using distance and turned angle
    plan20.run();
      plan20.run();
     plan40.run();
     }
     //
    if (ini["plan40"]["run"] != "false")
    { // example odometry drive using distance and turned angle
      plan40.run();
     }
     mixer.setVelocity(0.0);
     mixer.setVelocity(0.0);
     mixer.setTurnrate(0.0);
     mixer.setTurnrate(0.0);
     sleep(1); // to allow robot to stop while logging is running
     sleep(1); // to allow the robot to stop while logging is running
     // turn off led 16
     // turn off led 16
     gpio.setPin(16, 0);
     gpio.setPin(16, 0);
Line 58: Line 54:


The main function handles:
The main function handles:
* '''setup''' of all modules by calling ''setup(...)'' in the service module.
* '''setup''' all modules by calling ''setup(...)'' in the service module.
* '''run''' the mission (or missions) needed.
* '''run''' the mission (or missions) needed.
* '''stop and terminate''' in a proper way; the service module handles the termination details.
* '''stop and terminate''' properly; the service module handles the termination details.


=== Run mission ===
=== Run mission ===
Line 66: Line 62:
The entire mission can be divided into smaller parts that can be tested individually.
The entire mission can be divided into smaller parts that can be tested individually.


One example could be ''plan20''.  
One example could be ''plan20'' in the example above in the '''main.cpp''' file.
 
int main(...)
{
    ...
    '''plan20.run'''();
    ...
}


    if (ini["plan20"]["run"] != "true")
The behaviour plan itself (in '''bplan20.cpp''') tests whether the plan should be active.
    { // example odometry drive using distance and turned angle
      '''plan20.run'''();
    }


It can be activated by setting the ''run'' flag to "true" in the ''robot.ini'' configuration file.
void BPlan20::run()
{
  if (not setupDone)
    setup();
  '''if (ini["plan20"]["run"] == "false")'''
  '''  return;'''
  bool finished = false;
  bool lost = false;
  ...
}
 
 
The '''ini["plan20"]["run"]''' refers to the configuration file.
 
The plan can be activated by setting the ''run'' flag to "true" in the ''robot.ini'' configuration file.


  ; part of robot.ini file
  ; part of robot.ini file
Line 81: Line 95:
  print = true
  print = true


The sequence of the entire mission is handled in this ''main'' function. In this case, the entire mission consists of ''plan20'' and ''plan40''.
The sequence of the entire mission is handled in this ''main'' function. In this case, the mission consists of ''plan20'' and ''plan40''.


You are, of course, allowed to change whatever you like, especially if you find more innovative ways to do it.
You are, of course, allowed to change whatever you like, especially if you find more innovative ways to do it.
Line 88: Line 102:
=== Copyright ===
=== Copyright ===


All software, specific for Robobot, is free to use and change, according to the MIT License. One of the least restrictive copyright types.
All software, specific for Robobot, is free to use and change, according to the MIT License, one of the least restrictive copyright types.


=== Include files ===
=== Include files ===


To call functions, the compiler needs to know where these functions are defined. C++ handles this by including the needed files prior to compiling.
The compiler needs to know where these functions are defined to call functions. C++ handles this by including the needed files before compiling.


Where to look for these files is indicated by the brackets, like
Where to look for these files is indicated by the brackets, like
Line 102: Line 116:


The second file ''bplan20.h'' is in ".." bracket, indicating that the file is in the same directory as the file that includes the file.
The second file ''bplan20.h'' is in ".." bracket, indicating that the file is in the same directory as the file that includes the file.
== Compile using CMake ==
To compile, the compiler uses a lot of parameters to specify what to compile, where the system libraries are placed and other compile options.
A Makefile is often used to specify these options, to check for dependencies and not to compile more than needed.
A Makefile can be complicated too, and a number of apps exist to simplify the generation of the Makefile.
One of the most widely used is CMake
=== CMake ===
CMake has a specification file called ''CMakeLists.txt'', for our project, it looks like this:
cmake_minimum_required(VERSION 3.8)
project('''raubase''')
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()
find_package('''OpenCV''' REQUIRED )
find_package(Threads REQUIRED)
#find_package(libgpiodcxx REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS} ${rclcpp_INCLUDE_DIRS} ${dlib_INCLUDE_DIR})
execute_process(COMMAND uname -m RESULT_VARIABLE IS_OK OUTPUT_VARIABLE CPU1)
string(STRIP ${CPU1} CPU)
# works for Raspberry 3 and 4
if (${CPU} MATCHES "armv7l" OR ${CPU} MATCHES "aarch64")
    message("# Is a RASPBERRY; CPU=${CPU} (Pi3=armv7l, pi4=aarch64)")
    #    set(EXTRA_CC_FLAGS " -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -DRASPBERRY_PI -D${CPU}")
    set(EXTRA_CC_FLAGS "-D${CPU} -O2 -g0 -DRASPBERRY_PI -I/home/local/git/CLI11/include")
    #set(EXTRA_CC_FLAGS "-D${CPU} -O0 -g2 -DRASPBERRY_PI")
  else()
    message("# Not a RASPBERRY; CPU=${CPU}")
    set(EXTRA_CC_FLAGS "-D${CPU} -O0 -g2")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic \
    -Wno-format-truncation -Wno-return-type \
    -std=c++20 ${EXTRA_CC_FLAGS}")
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} "-pthread")
add_executable('''raubase'''
      src/bplan20.cpp
      src/bplan21.cpp
      src/bplan40.cpp
      src/cedge.cpp
      src/cheading.cpp
      src/cmixer.cpp
      src/cmotor.cpp
      src/cservo.cpp
      src/main.cpp
      src/medge.cpp
      src/mpose.cpp
      src/sedge.cpp
      src/sencoder.cpp
      src/sgpiod.cpp
      src/simu.cpp
      src/sdist.cpp
      src/sjoylogitech.cpp
      src/spyvision.cpp
      src/sstate.cpp
      src/steensy.cpp
      src/upid.cpp
      src/uservice.cpp
      src/usocket.cpp
      src/utime.cpp
      )
if (${CPU} MATCHES "armv7l" OR ${CPU} MATCHES "aarch64")
  target_link_libraries('''raubase''' ${CMAKE_THREAD_LIBS_INIT} readline gpiod rt)
else()
  target_link_libraries('''raubase''' ${CMAKE_THREAD_LIBS_INIT} readline gpiod)
endif()
This file defines the name of the executable: ''raubase''.
Adaptation to Raspberry Pi is handled in a few ''if'' sections. This allows the software to be compiled and run on other platforms; this has been an advantage, especially during the initial debugging.
What to compile is specified here. If you add new mission plans (or other functions), such files need to be added to this list.
=== Build directory ===
Both CMake and Make generate a lot of files to make the compilation faster. In order not to clutter the base directory, it is recommended to make a build directory as a sub-directory to the base directory.
This build directory is already present in the Raspberry disk for the Robobot:
$ cd ~/svn/robobot/raubase/build
If this directory is empty, or the ''CMakeLists.txt'' is changed substantially, then CMake is needed to generate or regenerate the Makefile.
$ cd ~/svn/robobot/raubase/build
$ cmake ..
The ".." means parent directory, and tells ''cmake'' where to find the ''CMakeLists.txt'' file.
The ''CMake'' list the progress, like this:
$ cmake ..
-- The C compiler identification is GNU 10.2.1
-- The CXX compiler identification is GNU 10.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found OpenCV: /usr (found version "4.5.1")
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed
-- Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in thread
-- Looking for pthread_create in pthread - found
-- Found Threads: TRUE 
# Is a RASPBERRY; CPU=armv7l (Pi3=armv7l, pi4=aarch64)
-- Configuring done
-- Generating done
-- Build files have been written to: /home/local/svn/robobot/raubase/build
CMake is also looking for some non-relevant features, like PTHREAD as C-library CMAKE_HAVE_LIBC_PTHREAD, but this is not used here, so that is OK.
It detects that it is running on a Raspberry PI version 3.
There is now a Makefile in the ''build'' directory, but there are other files too:
$ ls
CMakeCache.txt  CMakeFiles  '''Makefile'''  cmake_install.cmake
The Makefile is long and almost unreadable, but we just need it to compile.
It will also check if the ''CMakeLists.txt'' file is changed, and re-run CMake if needed.
So just make the executable using the Makefile, here is added a "-j3" option to allow the compiler to use 3 CPU kernels to the compile task:
$ make -j3
[ 12%] Building CXX object CMakeFiles/raubase.dir/src/bplan21.cpp.o
[ 12%] Building CXX object CMakeFiles/raubase.dir/src/bplan40.cpp.o
[ 12%] Building CXX object CMakeFiles/raubase.dir/src/bplan20.cpp.o
[ 16%] Building CXX object CMakeFiles/raubase.dir/src/cedge.cpp.o
[ 20%] Building CXX object CMakeFiles/raubase.dir/src/cheading.cpp.o
[ 24%] Building CXX object CMakeFiles/raubase.dir/src/cmixer.cpp.o
[ 28%] Building CXX object CMakeFiles/raubase.dir/src/cmotor.cpp.o
[ 32%] Building CXX object CMakeFiles/raubase.dir/src/cservo.cpp.o
[ 36%] Building CXX object CMakeFiles/raubase.dir/src/main.cpp.o
[ 40%] Building CXX object CMakeFiles/raubase.dir/src/medge.cpp.o
[ 44%] Building CXX object CMakeFiles/raubase.dir/src/mpose.cpp.o
[ 48%] Building CXX object CMakeFiles/raubase.dir/src/sedge.cpp.o
[ 52%] Building CXX object CMakeFiles/raubase.dir/src/sencoder.cpp.o
[ 56%] Building CXX object CMakeFiles/raubase.dir/src/sgpiod.cpp.o
[ 60%] Building CXX object CMakeFiles/raubase.dir/src/simu.cpp.o
[ 64%] Building CXX object CMakeFiles/raubase.dir/src/sdist.cpp.o
[ 68%] Building CXX object CMakeFiles/raubase.dir/src/sjoylogitech.cpp.o
[ 72%] Building CXX object CMakeFiles/raubase.dir/src/spyvision.cpp.o
[ 76%] Building CXX object CMakeFiles/raubase.dir/src/sstate.cpp.o
[ 80%] Building CXX object CMakeFiles/raubase.dir/src/steensy.cpp.o
[ 84%] Building CXX object CMakeFiles/raubase.dir/src/upid.cpp.o
[ 88%] Building CXX object CMakeFiles/raubase.dir/src/uservice.cpp.o
[ 92%] Building CXX object CMakeFiles/raubase.dir/src/usocket.cpp.o
/home/local/svn/robobot/raubase/src/steensy.cpp: In member function ‘bool UOutQueue::setMessage(const char*)’:
/home/local/svn/robobot/raubase/src/steensy.cpp:65:12: warning: ‘char* strncpy(char*, const char*, size_t)’ output may be truncated copying 3 bytes from a string of length 3 [-Wstringop-truncation]
    65 |    strncpy(msg, cc, 3);
      |    12:06, 31 December 2023 (CET)~~^12:06, 31 December 2023 (CET)12:06, 31 December 2023 (CET)~
[ 96%] Building CXX object CMakeFiles/raubase.dir/src/utime.cpp.o
[100%] Linking CXX executable raubase
[100%] Built target raubase
There is a single warning, but I couldn't find a simple way to avoid the warning, and it has no negative consequences other than the warning.
There is now a '''raubase''' executable:
$ ls
CMakeCache.txt  CMakeFiles  Makefile  cmake_install.cmake  '''raubase'''
If you run the executable with a "--help" option, you get:
$ ./raubase --help
ROBOBOT app
Usage: ./raubase [OPTIONS]
Options:
  -h,--help                  Print this help message and exit
  -v,--version                Latest SVN version (for uservice.cpp)
  -a,--api-list              List available modules
  -d,--daemon                Do not listen to the keyboard (daemon mode)
  -w,--white                  Calibrate line sensor on white surface
  -b,--black                  Calibrate line sensor on black surface
  -n,--number INT            Set robot number to Regbot part [0..150]
This is mostly helpful for line sensor calibration.

Latest revision as of 10:08, 23 January 2024

Back to Robobot B.

Back to Robobot software description

Main

C++ starts executing a function called main(int argc, char ** argv). In Robobot, this is relatively simple, and the intention is explained here.

The main.cpp file looks like this (slightly reduced):

/*
#***************************************************************************
#*   Copyright (C) 2023 by DTU
#*   jcan@dtu.dk
#*
#* The MIT License (MIT)  https://mit-license.org/
#***************************************************************************/
// System libraries
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
//
// Include local files for data values and functions
#include "uservice.h"
#include "cmixer.h"
#include "sgpiod.h"
#include "bplan20.h"
#include "bplan40.h"

int main (int argc, char **argv)
{ // prepare all modules and start data flow
  bool setupOK = service.setup(argc, argv);
  if (setupOK)
  { // turn on LED on port 16
    gpio.setPin(16, 1);
    //
    // Run the behaviour plans in this sequence
    plan20.run();
    plan40.run();
    //
    mixer.setVelocity(0.0);
    mixer.setTurnrate(0.0);
    sleep(1); // to allow the robot to stop while logging is running
    // turn off led 16
    gpio.setPin(16, 0);
  }
  // close all logfiles
  service.terminate();
}

The main() function

The main function handles:

  • setup all modules by calling setup(...) in the service module.
  • run the mission (or missions) needed.
  • stop and terminate properly; the service module handles the termination details.

Run mission

The entire mission can be divided into smaller parts that can be tested individually.

One example could be plan20 in the example above in the main.cpp file.

int main(...)
{
   ...
   plan20.run();
   ...
}

The behaviour plan itself (in bplan20.cpp) tests whether the plan should be active.

void BPlan20::run()
{
  if (not setupDone)
    setup();
  if (ini["plan20"]["run"] == "false")
    return;
 bool finished = false;
 bool lost = false;
 ...
}


The ini["plan20"]["run"] refers to the configuration file.

The plan can be activated by setting the run flag to "true" in the robot.ini configuration file.

; part of robot.ini file
[plan20]
log = true
run = true
print = true

The sequence of the entire mission is handled in this main function. In this case, the mission consists of plan20 and plan40.

You are, of course, allowed to change whatever you like, especially if you find more innovative ways to do it. The main objective has been to make it readable, understandable and then functional.

Copyright

All software, specific for Robobot, is free to use and change, according to the MIT License, one of the least restrictive copyright types.

Include files

The compiler needs to know where these functions are defined to call functions. C++ handles this by including the needed files before compiling.

Where to look for these files is indicated by the brackets, like

#include <string>
#include "bplan20.h"

The first file included is string in <..> brackets, indicating that this is a system library, and the path for such libraries is part of the compile parameters.

The second file bplan20.h is in ".." bracket, indicating that the file is in the same directory as the file that includes the file.