* This file implements Beremiz C++ runtime Command Line Interface for POSIX
* Based on erpcsniffer.cpp, BSD-3-Clause, Copyright 2017 NXP
* Copyright 2024 Beremiz SAS
* See COPYING for licensing details
#include "erpc_basic_codec.hpp"
#include "erpc_serial_transport.hpp"
#include "erpc_tcp_transport.hpp"
#include "erpc_simple_server.hpp"
// eRPC generated includes
#include "erpc_PLCObject_server.hpp"
// erpcgen includes (re-uses erpcgen's logging system and options parser)
class MyMessageBufferFactory : public MessageBufferFactory
virtual MessageBuffer create()
uint8_t *buf = new uint8_t[MSG_SIZE];
return MessageBuffer(buf, MSG_SIZE);
virtual void dispose(MessageBuffer *buf)
namespace beremizRuntime {
const char k_toolName[] = "beremizRuntime";
/*! Current version number for the tool. */
const char k_version[] = __STRING(BEREMIZ_VERSION);
const char k_copyright[] = "Copyright 2024 Beremiz SAS. All rights reserved.";
static const char *k_optionsDefinition[] = { "?|help",
"t:transport <transport>",
const char k_usageText[] =
-?/--help Show this help\n\
-V/--version Display tool version\n\
-v/--verbose Print extra detailed log information\n\
-t/--transport <transport> Type of transport.\n\
-b/--baudrate <baudrate> Baud rate.\n\
-p/--port <port> Port name or port number.\n\
-h/--host <host> Host definition.\n\
-a/--autoload Autoload.\n\
Available transports (use with -t option):\n\
tcp Tcp transport type (host, port number).\n\
serial Serial transport type (port name, baud rate).\n\
* @brief Class that encapsulates the beremizRuntime tool.
* A single global logger instance is created during object construction. It is
* never freed because we need it up to the last possible minute, when an
* exception could be thrown.
enum class verbose_type_t
}; /*!< Types of verbose outputs from beremizRuntime application. */
}; /*!< Type of transport to use. */
typedef vector<string> string_vector_t;
int m_argc; /*!< Number of command line arguments. */
char **m_argv; /*!< String value for each command line argument. */
StdoutLogger *m_logger; /*!< Singleton logger instance. */
verbose_type_t m_verboseType; /*!< Which type of log is need to set (warning, info, debug). */
const char *m_workingDir; /*!< working directory. */
string_vector_t m_positionalArgs;
transports_t m_transport; /*!< Transport used for receiving messages. */
uint32_t m_baudrate; /*!< Baudrate rate speed. */
const char *m_port; /*!< Name or number of port. Based on used transport. */
const char *m_host; /*!< Host name */
bool m_autoload = false; /*!< Autoload flag. */
* @param[in] argc Count of arguments in argv variable.
* @param[in] argv Pointer to array of arguments.
* Creates the singleton logger instance.
beremizRuntimeCLI(int argc, char *argv[]) :
m_argc(argc), m_argv(argv), m_logger(0), m_verboseType(verbose_type_t::kWarning),
m_workingDir(NULL), m_transport(transports_t::kNoneTransport), m_baudrate(115200), m_port(NULL),
// create logger instance
m_logger = new StdoutLogger();
m_logger->setFilterLevel(Logger::log_level_t::kWarning);
Log::setLogger(m_logger);
* @brief Reads the command line options passed into the constructor.
* This method can return a return code to its caller, which will cause the
* tool to exit immediately with that return code value. Normally, though, it
* will return -1 to signal that the tool should continue to execute and
* all options were processed successfully.
* The Options class is used to parse command line options. See
* #k_optionsDefinition for the list of options and #k_usageText for the
* descriptive help for each option.
* @retval -1 The options were processed successfully. Let the tool run normally.
* @return A zero or positive result is a return code value that should be
* returned from the tool as it exits immediately.
Options options(*m_argv, k_optionsDefinition);
OptArgvIter iter(--m_argc, ++m_argv);
// process command line options
while ((optchar = options(iter, optarg)))
printf("%s %s\n%s\n", k_toolName, k_version, k_copyright);
if (m_verboseType != verbose_type_t::kExtraDebug)
m_verboseType = (verbose_type_t)(((int)m_verboseType) + 1);
string transport = optarg;
m_transport = transports_t::kTcpTransport;
else if (transport == "serial")
m_transport = transports_t::kSerialTransport;
Log::error("error: unknown transport type %s", transport.c_str());
m_baudrate = strtoul(optarg, NULL, 10);
Log::error("error: unrecognized option\n\n");
// handle positional args
if (iter.index() < m_argc)
if (m_argc - iter.index() > 1){
Log::error("error: too many arguments\n\n");
for (i = iter.index(); i < m_argc; ++i)
m_positionalArgs.push_back(m_argv[i]);
* @brief Prints help for the tool.
* @param[in] options Options, which can be used.
void printUsage(Options &options)
options.usage(cout, "[path]");
* @brief Core of the tool.
* Calls processOptions() to handle command line options before performing the
* real work the tool does.
* @retval 1 The functions wasn't processed successfully.
* @retval 0 The function was processed successfully.
* @exception Log::error This function is called, when function wasn't
* processed successfully.
* @exception runtime_error Thrown, when positional args is empty.
// read command line options
if ((result = processOptions()) != -1)
if (!m_positionalArgs.size())
m_workingDir = std::filesystem::current_path().c_str();
m_workingDir = m_positionalArgs[0].c_str();
std::filesystem::current_path(m_workingDir);
// remove temporary directory if it already exists
if (std::filesystem::exists("tmp"))
std::filesystem::remove_all("tmp");
// Create temporary directory in working directory
std::filesystem::create_directory("tmp");
case transports_t::kTcpTransport:
uint16_t portNumber = strtoul(m_port, NULL, 10);
TCPTransport *tcpTransport = new TCPTransport(m_host, portNumber, true);
if (erpc_status_t err = tcpTransport->open())
_transport = tcpTransport;
case transports_t::kSerialTransport:
SerialTransport *serialTransport = new SerialTransport(m_port, m_baudrate);
while (kErpcStatus_Success != serialTransport->init(vtime, vmin))
_transport = serialTransport;
_transport->setCrc16(&crc);
MyMessageBufferFactory _msgFactory;
BasicCodecFactory _basicCodecFactory;
Log::info("Starting ERPC server...\n");
_server.setMessageBufferFactory(&_msgFactory);
_server.setTransport(_transport);
_server.setCodecFactory(&_basicCodecFactory);
PLCObject plc_object = PLCObject();
BeremizPLCObjectService_service svc = BeremizPLCObjectService_service(&plc_object);
_server.addService(&svc);
Log::error("error: %s\n", e.what());
Log::error("error: unexpected exception\n");
* @brief Turns on verbose logging.
// verbose only affects the INFO and DEBUG filter levels
// if the user has selected quiet mode, it overrides verbose
case verbose_type_t::kWarning:
Log::getLogger()->setFilterLevel(Logger::log_level_t::kWarning);
case verbose_type_t::kInfo:
Log::getLogger()->setFilterLevel(Logger::log_level_t::kInfo);
case verbose_type_t::kDebug:
Log::getLogger()->setFilterLevel(Logger::log_level_t::kDebug);
case verbose_type_t::kExtraDebug:
Log::getLogger()->setFilterLevel(Logger::log_level_t::kDebug2);
} // namespace beremizRuntime
* @brief Main application entry point.
* Creates a tool instance and lets it take over.
int main(int argc, char *argv[], char *envp[])
return beremizRuntime::beremizRuntimeCLI(argc, argv).run();
Log::error("error: unexpected exception\n");