C_runtime/posix_main.cpp
changeset 3937 e13543d716b6
equal deleted inserted replaced
3936:129202e555e0 3937:e13543d716b6
       
     1 
       
     2 #include <stdlib.h>
       
     3 #include <vector>
       
     4 #include <filesystem>
       
     5 
       
     6 #include "erpc_basic_codec.hpp"
       
     7 #include "erpc_serial_transport.hpp"
       
     8 #include "erpc_tcp_transport.hpp"
       
     9 #include "erpc_simple_server.hpp"
       
    10 
       
    11 #include "erpc_PLCObject_server.hpp"
       
    12 #include "Logging.hpp"
       
    13 #include "options.hpp"
       
    14 
       
    15 #include "PLCObject.hpp"
       
    16 
       
    17 using namespace erpc;
       
    18 using namespace std;
       
    19 
       
    20 class MyMessageBufferFactory : public MessageBufferFactory
       
    21 {
       
    22 public:
       
    23     virtual MessageBuffer create()
       
    24     {
       
    25         uint8_t *buf = new uint8_t[1024];
       
    26         return MessageBuffer(buf, 1024);
       
    27     }
       
    28 
       
    29     virtual void dispose(MessageBuffer *buf)
       
    30     {
       
    31         erpc_assert(buf);
       
    32         if (*buf)
       
    33         {
       
    34             delete[] buf->get();
       
    35         }
       
    36     }
       
    37 };
       
    38 
       
    39 
       
    40 namespace beremizRuntime {
       
    41 
       
    42 /*! The tool's name. */
       
    43 const char k_toolName[] = "beremizRuntime";
       
    44 
       
    45 /*! Current version number for the tool. */
       
    46 const char k_version[] = __STRING(BEREMIZ_VERSION);
       
    47 
       
    48 /*! Copyright string. */
       
    49 const char k_copyright[] = "Copyright 2024 Beremiz SAS. All rights reserved.";
       
    50 
       
    51 static const char *k_optionsDefinition[] = { "?|help",
       
    52                                              "V|version",
       
    53                                              "v|verbose",
       
    54                                              "t:transport <transport>",
       
    55                                              "b:baudrate <baudrate>",
       
    56                                              "p:port <port>",
       
    57                                              "h:host <host>",
       
    58                                              NULL };
       
    59 
       
    60 /*! Help string. */
       
    61 const char k_usageText[] =
       
    62     "\nOptions:\n\
       
    63   -?/--help                    Show this help\n\
       
    64   -V/--version                 Display tool version\n\
       
    65   -v/--verbose                 Print extra detailed log information\n\
       
    66   -t/--transport <transport>   Type of transport.\n\
       
    67   -b/--baudrate <baudrate>     Baud rate.\n\
       
    68   -p/--port <port>             Port name or port number.\n\
       
    69   -h/--host <host>             Host definition.\n\
       
    70 \n\
       
    71 Available transports (use with -t option):\n\
       
    72   tcp      Tcp transport type (host, port number).\n\
       
    73   serial   Serial transport type (port name, baud rate).\n\
       
    74 \n";
       
    75 
       
    76 
       
    77 /*!
       
    78  * @brief Class that encapsulates the beremizRuntime tool.
       
    79  *
       
    80  * A single global logger instance is created during object construction. It is
       
    81  * never freed because we need it up to the last possible minute, when an
       
    82  * exception could be thrown.
       
    83  */
       
    84 class beremizRuntimeCLI
       
    85 {
       
    86 protected:
       
    87     enum class verbose_type_t
       
    88     {
       
    89         kWarning,
       
    90         kInfo,
       
    91         kDebug,
       
    92         kExtraDebug
       
    93     }; /*!< Types of verbose outputs from beremizRuntime application. */
       
    94 
       
    95     enum class transports_t
       
    96     {
       
    97         kNoneTransport,
       
    98         kTcpTransport,
       
    99         kSerialTransport
       
   100     }; /*!< Type of transport to use. */
       
   101 
       
   102     typedef vector<string> string_vector_t;
       
   103 
       
   104     int m_argc;                   /*!< Number of command line arguments. */
       
   105     char **m_argv;                /*!< String value for each command line argument. */
       
   106     StdoutLogger *m_logger;       /*!< Singleton logger instance. */
       
   107     verbose_type_t m_verboseType; /*!< Which type of log is need to set (warning, info, debug). */
       
   108     const char *m_workingDir;     /*!< working directory. */
       
   109     string_vector_t m_positionalArgs;
       
   110     transports_t m_transport; /*!< Transport used for receiving messages. */
       
   111     uint32_t m_baudrate;      /*!< Baudrate rate speed. */
       
   112     const char *m_port;       /*!< Name or number of port. Based on used transport. */
       
   113     const char *m_host;       /*!< Host name */
       
   114 
       
   115 public:
       
   116     /*!
       
   117      * @brief Constructor.
       
   118      *
       
   119      * @param[in] argc Count of arguments in argv variable.
       
   120      * @param[in] argv Pointer to array of arguments.
       
   121      *
       
   122      * Creates the singleton logger instance.
       
   123      */
       
   124     beremizRuntimeCLI(int argc, char *argv[]) :
       
   125     m_argc(argc), m_argv(argv), m_logger(0), m_verboseType(verbose_type_t::kWarning),
       
   126     m_workingDir(NULL), m_transport(transports_t::kNoneTransport), m_baudrate(115200), m_port(NULL),
       
   127     m_host(NULL)
       
   128     {
       
   129         // create logger instance
       
   130         m_logger = new StdoutLogger();
       
   131         m_logger->setFilterLevel(Logger::log_level_t::kWarning);
       
   132         Log::setLogger(m_logger);
       
   133     }
       
   134 
       
   135     /*!
       
   136      * @brief Destructor.
       
   137      */
       
   138     ~beremizRuntimeCLI() {}
       
   139 
       
   140     /*!
       
   141      * @brief Reads the command line options passed into the constructor.
       
   142      *
       
   143      * This method can return a return code to its caller, which will cause the
       
   144      * tool to exit immediately with that return code value. Normally, though, it
       
   145      * will return -1 to signal that the tool should continue to execute and
       
   146      * all options were processed successfully.
       
   147      *
       
   148      * The Options class is used to parse command line options. See
       
   149      * #k_optionsDefinition for the list of options and #k_usageText for the
       
   150      * descriptive help for each option.
       
   151      *
       
   152      * @retval -1 The options were processed successfully. Let the tool run normally.
       
   153      * @return A zero or positive result is a return code value that should be
       
   154      *      returned from the tool as it exits immediately.
       
   155      */
       
   156     int processOptions()
       
   157     {
       
   158         Options options(*m_argv, k_optionsDefinition);
       
   159         OptArgvIter iter(--m_argc, ++m_argv);
       
   160 
       
   161         // process command line options
       
   162         int optchar;
       
   163         const char *optarg;
       
   164         while ((optchar = options(iter, optarg)))
       
   165         {
       
   166             switch (optchar)
       
   167             {
       
   168                 case '?':
       
   169                 {
       
   170                     printUsage(options);
       
   171                     return 0;
       
   172                 }
       
   173 
       
   174                 case 'V':
       
   175                 {
       
   176                     printf("%s %s\n%s\n", k_toolName, k_version, k_copyright);
       
   177                     return 0;
       
   178                 }
       
   179 
       
   180                 case 'v':
       
   181                 {
       
   182                     if (m_verboseType != verbose_type_t::kExtraDebug)
       
   183                     {
       
   184                         m_verboseType = (verbose_type_t)(((int)m_verboseType) + 1);
       
   185                     }
       
   186                     break;
       
   187                 }
       
   188 
       
   189                 case 't':
       
   190                 {
       
   191                     string transport = optarg;
       
   192                     if (transport == "tcp")
       
   193                     {
       
   194                         m_transport = transports_t::kTcpTransport;
       
   195                     }
       
   196                     else if (transport == "serial")
       
   197                     {
       
   198                         m_transport = transports_t::kSerialTransport;
       
   199                     }
       
   200                     else
       
   201                     {
       
   202                         Log::error("error: unknown transport type %s", transport.c_str());
       
   203                         return 1;
       
   204                     }
       
   205                     break;
       
   206                 }
       
   207 
       
   208                 case 'b':
       
   209                 {
       
   210                     m_baudrate = strtoul(optarg, NULL, 10);
       
   211                     break;
       
   212                 }
       
   213 
       
   214                 case 'p':
       
   215                 {
       
   216                     m_port = optarg;
       
   217                     break;
       
   218                 }
       
   219 
       
   220                 case 'h':
       
   221                 {
       
   222                     m_host = optarg;
       
   223                     break;
       
   224                 }
       
   225 
       
   226                 default:
       
   227                 {
       
   228                     Log::error("error: unrecognized option\n\n");
       
   229                     printUsage(options);
       
   230                     return 0;
       
   231                 }
       
   232             }
       
   233         }
       
   234 
       
   235         // handle positional args
       
   236         if (iter.index() < m_argc)
       
   237         {
       
   238             if (m_argc - iter.index() > 1){
       
   239                 Log::error("error: too many arguments\n\n");
       
   240                 printUsage(options);
       
   241                 return 0;
       
   242             }
       
   243             int i;
       
   244             for (i = iter.index(); i < m_argc; ++i)
       
   245             {
       
   246                 m_positionalArgs.push_back(m_argv[i]);
       
   247             }
       
   248         }
       
   249 
       
   250         // all is well
       
   251         return -1;
       
   252     }
       
   253 
       
   254     /*!
       
   255      * @brief Prints help for the tool.
       
   256      *
       
   257      * @param[in] options Options, which can be used.
       
   258      */
       
   259     void printUsage(Options &options)
       
   260     {
       
   261         options.usage(cout, "[path]");
       
   262         printf(k_usageText);
       
   263     }
       
   264 
       
   265     /*!
       
   266      * @brief Core of the tool.
       
   267      *
       
   268      * Calls processOptions() to handle command line options before performing the
       
   269      * real work the tool does.
       
   270      *
       
   271      * @retval 1 The functions wasn't processed successfully.
       
   272      * @retval 0 The function was processed successfully.
       
   273      *
       
   274      * @exception Log::error This function is called, when function wasn't
       
   275      *              processed successfully.
       
   276      * @exception runtime_error Thrown, when positional args is empty.
       
   277      */
       
   278     int run()
       
   279     {
       
   280         try
       
   281         {
       
   282             // read command line options
       
   283             int result;
       
   284             if ((result = processOptions()) != -1)
       
   285             {
       
   286                 return result;
       
   287             }
       
   288 
       
   289             // set verbose logging
       
   290             setVerboseLogging();
       
   291 
       
   292             if (!m_positionalArgs.size())
       
   293             {
       
   294                 m_workingDir = std::filesystem::current_path().c_str();
       
   295             } else {
       
   296                 m_workingDir = m_positionalArgs[0].c_str();
       
   297             }
       
   298 
       
   299             Transport *_transport;
       
   300             switch (m_transport)
       
   301             {
       
   302                 case transports_t::kTcpTransport:
       
   303                 {
       
   304                     uint16_t portNumber = strtoul(m_port, NULL, 10);
       
   305                     TCPTransport *tcpTransport = new TCPTransport(m_host, portNumber, true);
       
   306                     if (erpc_status_t err = tcpTransport->open())
       
   307                     {
       
   308                         return err;
       
   309                     }
       
   310                     _transport = tcpTransport;
       
   311                     break;
       
   312                 }
       
   313 
       
   314                 case transports_t::kSerialTransport:
       
   315                 {
       
   316                     SerialTransport *serialTransport = new SerialTransport(m_port, m_baudrate);
       
   317 
       
   318                     uint8_t vtime = 0;
       
   319                     uint8_t vmin = 1;
       
   320                     while (kErpcStatus_Success != serialTransport->init(vtime, vmin))
       
   321                         ;
       
   322 
       
   323                     _transport = serialTransport;
       
   324                     break;
       
   325                 }
       
   326 
       
   327                 default:
       
   328                 {
       
   329                     break;
       
   330                 }
       
   331             }
       
   332 
       
   333             MyMessageBufferFactory _msgFactory;
       
   334             BasicCodecFactory _basicCodecFactory;
       
   335             SimpleServer _server;
       
   336 
       
   337             BeremizPLCObjectService_service *svc;
       
   338 
       
   339             Log::info("Starting ERPC server...\n");
       
   340 
       
   341             _server.setMessageBufferFactory(&_msgFactory);
       
   342             _server.setTransport(_transport);
       
   343             _server.setCodecFactory(&_basicCodecFactory);
       
   344 
       
   345             svc = new BeremizPLCObjectService_service(new PLCObject());
       
   346 
       
   347             _server.addService(svc);
       
   348 
       
   349             _server.run();
       
   350 
       
   351             return 0;
       
   352         }
       
   353         catch (exception &e)
       
   354         {
       
   355             Log::error("error: %s\n", e.what());
       
   356             return 1;
       
   357         }
       
   358         catch (...)
       
   359         {
       
   360             Log::error("error: unexpected exception\n");
       
   361             return 1;
       
   362         }
       
   363 
       
   364         return 0;
       
   365     }
       
   366 
       
   367     /*!
       
   368      * @brief Turns on verbose logging.
       
   369      */
       
   370     void setVerboseLogging()
       
   371     {
       
   372         // verbose only affects the INFO and DEBUG filter levels
       
   373         // if the user has selected quiet mode, it overrides verbose
       
   374         switch (m_verboseType)
       
   375         {
       
   376             case verbose_type_t::kWarning:
       
   377                 Log::getLogger()->setFilterLevel(Logger::log_level_t::kWarning);
       
   378                 break;
       
   379             case verbose_type_t::kInfo:
       
   380                 Log::getLogger()->setFilterLevel(Logger::log_level_t::kInfo);
       
   381                 break;
       
   382             case verbose_type_t::kDebug:
       
   383                 Log::getLogger()->setFilterLevel(Logger::log_level_t::kDebug);
       
   384                 break;
       
   385             case verbose_type_t::kExtraDebug:
       
   386                 Log::getLogger()->setFilterLevel(Logger::log_level_t::kDebug2);
       
   387                 break;
       
   388         }
       
   389     }
       
   390 };
       
   391 
       
   392 } // namespace beremizRuntime
       
   393 
       
   394 /*!
       
   395  * @brief Main application entry point.
       
   396  *
       
   397  * Creates a tool instance and lets it take over.
       
   398  */
       
   399 int main(int argc, char *argv[], char *envp[])
       
   400 {
       
   401     (void)envp;
       
   402     try
       
   403     {
       
   404         return beremizRuntime::beremizRuntimeCLI(argc, argv).run();
       
   405     }
       
   406     catch (...)
       
   407     {
       
   408         Log::error("error: unexpected exception\n");
       
   409         return 1;
       
   410     }
       
   411 
       
   412     return 0;
       
   413 }