How to print filename and function name using spdlog? - c++

https://spdlog.docsforge.com/v1.x/3.custom-formatting/#customizing-format-using-set_pattern
Following spdlog documentation I was able to modify the print pattern
#include "spdlog/spdlog.h"
#include "spdlog/sinks/daily_file_sink.h"
int main()
{
spdlog::set_pattern("%H:%M:%S.%e %t %s %! %v");
auto logger = spdlog::daily_logger_mt("daily_logger", "../../../logs/daily.txt", 2, 30);
logger->info("casdc");
return 0;
}
With the above I see daily_logger.txt contents below:
01:41:35.895 70101 casdc
01:44:22.719 71978 casdc
01:44:35.225 72232 casdc
But I was expecting to see also:
%s Basename of the source file
%! Source function
Why I'm not seeing those?
Actually it seems to work if I call
SPDLOG_LOGGER_INFO(logger, "{:>10}", "vsvfsdfv");
But then my question is when should I call
logger->info("casdc");

Related

Parse the .dbc file and generate C++ code to represent classes/struct for each message( for target ECU )

I am trying to generate C++ code from a .dbc file.
e.g. A message is defined like following in .dbc file
BO_ 500 IO_DEBUG: 5 IO
SG_ IO_DEBUG_test_unsigned : 0|8#1+ (1,0) [0|0] "" DBG
SG_ IO_DEBUG_test_signed : 8|8#1- (1,-128) [0|0] "" DBG
SG_ IO_DEBUG_test_float1 : 16|8#1+ (0.1,0) [0|0] "" DBG
SG_ IO_DEBUG_test_float2 : 24|12#1+ (0.01,-20.48) [-20.48|20.47] "" DBG
SG_ IO_DEBUG_test_enum : 38|2#1+ (1,0) [0|0] "" DBG
BA_ "FieldType" SG_ 500 IO_DEBUG_test_enum "IO_DEBUG_test_enum";
VAL_ 500 IO_DEBUG_test_enum 2 "IO_DEBUG_test2_enum_two" 1 "IO_DEBUG_test2_enum_one" ;
I am trying to generate C++ code something like this. Message name will become the Class name and all signals should become the members of the class along with data-types.
//IoDebug.h -- ProcessMessageInterface is an interface.
class IoDebug : public ProcessMessageInterface {
pubic:
// ProcessMessageInterface implementation
void processMessage();
private:
uint8_t testUnSigned;
int8_t testSigned;
float testFloat1;
float testFloat2;
IO_DEBUG_test_enum testEnum;
};
//IoDebug.cpp
#include "IoDebug.h"
IoDebug::processMessage()
{
}
Is there any dbc parser and code generation tool(s) exists which can generate code like above?
This is the closest thing I have found:
https://github.com/astand/c-coderdbc
It seems to generate code in roughly the same format as you desire.
There is also a website associated with it:
https://coderdbc.com/ccoder/uploaddbc
There is also this other similar project:
https://github.com/xR3b0rn/dbcppp
But I personally did not like the generated code, since it doesn't create structs for each CAN message, and instead parses each signal individually. This approach probably works fine, but isn't quite what you are looking for.
Here is a python script which generates C++ code. You need to install cantools packages to run following script.
import cantools
import math
def build_name(name):
nodes = name.split("_")
nodes[0] = nodes[0].title()
return "".join(nodes)
def signal_variable_name(signal_name):
return "m_" + build_name(signal_name)
def isFloat(signal):
return True if isinstance(signal.scale, float) else False
def signal_data_type(signal):
if not signal.choices:
if isFloat(signal):
return "float"
else:
return "int" if signal.is_signed else "uint" + str((math.floor((signal.length - 1) / 8) + 1) * 8) + "_t"
else:
return signal.name
def initial_signal_value(signal):
initial = 0
if signal.initial:
initial = signal.initial
print("initial: "+str(initial))
print(signal.choices)
if signal.choices:
return signal.name + "_" + signal.choices[initial]
else:
return initial
cpp_template = """
#include <string>
#include "{messagename}.h"
using namespace std;
{messagename}::{messagename}()
{{
}}
"""
header_template = """
#ifndef {message_h}
#define {message_h}
#include <stdint.h>
#include <iostream>
class {messagename} : public {messageparent} {{
public:
{messagename}();
bool processMessage();
private:
"""
# dbc file
db = cantools.database.load_file("path_to_dummy.dbc")
# We can grow following list, add those messages for which we want to generate the code.
messages_list=["IO_DEBUG"]
for message_name in messages_list:
# massaging message_name here.
good_message_name = build_name(message_name)
message = db.get_message_by_name(message_name)
message_cpp_file = good_message_name+".cpp"
context = {"messagename": good_message_name, "dbc_message_name": message_name}
# writing code for C++ file.
f = open(message_cpp_file, "w")
f.write(cpp_template.format(**context))
f.write("bool {}::processMessage() {{\n return true;\n}}\n".format(good_message_name))
# we can add more code here to auto-generate code inside above fucntion to process the signals.
f.close()
# writing code for header file.
message_header_file = good_message_name+".h"
f = open(message_header_file, "w")
context["message_h"] = message_name.upper()+"_H"
context["messageparent"] = "ProcessMessageInterface"
f.write(header_template.format(**context))
for signal in message.signals:
f.write(" {} {};\n".format(signal_data_type(signal), signal_variable_name(signal.name)))
f.write("\n};\n\n#endif // " + context["message_h"])
f.write("\n")
f.close()
run it as
python3 script.py
Above script will generate following header and cpp files.
IoDEBUG.h
#ifndef IO_DEBUG_H
#define IO_DEBUG_H
#include <stdint.h>
#include <iostream>
class IoDEBUG : public ProcessMessageInterface {
public:
IoDEBUG();
bool processMessage();
private:
uint8_t m_IoDEBUGtestunsigned;
int m_IoDEBUGtestsigned;
float m_IoDEBUGtestfloat1;
float m_IoDEBUGtestfloat2;
IO_DEBUG_test_enum m_IoDEBUGtestenum;
};
#endif // IO_DEBUG_H
IoDEBUG.cpp
#include <string>
#include "IoDEBUG.h"
using namespace std;
IoDEBUG::IoDEBUG()
{
}
bool IoDEBUG::processMessage() {
return true;
}
Please have a look at Soureforge's comFramework, too. It's likely close to what you need. Here, the concept is to make the output controlled by templates; the powerful template engine StringTemplate V4 is fed with the parsed DBC file. You can bring virtually everything from the DBC into C/C++ (messages, signals, attributes, enumerations, node names, etc.) Unfortunately, all samples still produce C. Migration to C++ is however trivial.
See https://sourceforge.net/projects/comframe/
Convert .dbc files to header files and c files for microcontroller using this windows software:
https://github.com/HamidBakhtiary/DBC_to_header
in description there is link to download software.
du to 25 MB limit it is not possible to put it in github.

How to use log4cxx RollingFileAppender on Windows

I'm trying to use log4cxx to log my application using RollingFileAppender on a Windows C++ console application. I would like to create a new log file every time the size reaches 1MB. Furthermore, when the desired size is reached, the file should be zipped automatically. The maximum number of files created must be 10; after which older files should be overwritten.
I'm using:
apache-log4cxx-0.10.0
apr-util-1.6.1
apr-1.7.0
This is my code:
log4cxx::rolling::RollingFileAppender* fileAppender1 = new log4cxx::rolling::RollingFileAppender();
fileAppender1->setLayout(log4cxx::LayoutPtr(new log4cxx::PatternLayout(L"[%d{ISO8601}{GMT}] %-4r [%t] %c | %-5p | %m%n")));
fileAppender1->setAppend(true);
log4cxx::helpers::Pool p;
fileAppender1->activateOptions(p);
log4cxx::rolling::FixedWindowRollingPolicy* rollingPolicy = new log4cxx::rolling::FixedWindowRollingPolicy();
rollingPolicy->setMinIndex(1);
rollingPolicy->setMaxIndex(10);
rollingPolicy->setFileNamePattern(L"j_log_%i.log");
log4cxx::rolling::SizeBasedTriggeringPolicy* triggerPolicy = new log4cxx::rolling::SizeBasedTriggeringPolicy();
triggerPolicy->setMaxFileSize(1024*1024);
fileAppender1->setRollingPolicy(rollingPolicy);
fileAppender1->setTriggeringPolicy(triggerPolicy);
LoggerPtr logger(Logger::getLogger("LogConsole1"));
logger->addAppender(fileAppender1);
logger->setLevel(log4cxx::Level::getTrace());
for (int i = 0; i < 10000; i++)
{
LOG4CXX_INFO(logger, "Created FileAppender appender");
LOG4CXX_INFO(logger, "LOGGER1");
}
The result obtained is a file named ".1" (without any extension) with such content (it seems ok):
[2019-09-13 07:44:58,619] 21063 [0x00003e14] LogConsole1 | INFO | Created FileAppender appender
[2019-09-13 07:44:58,622] 21066 [0x00003e14] LogConsole1 | INFO | LOGGER1
The problems are:
The file does not have the proper name
The file does not roll over (only one file is created also if its size exceeds 1MB)
On the application console I see many exceptions like: "log4cxx: Exception during rollover"
What am I doing wrong?
I do not completely understand your file pattern but the docs do not use the "L" char in their Pattern.
In my projects is use
rollingPolicy->setFileNamePattern("file.%i.log");
sometimes with a string variable which works good.
I can not find the configuration in your code snipped.
As far as i know, you have to setup the appender by using the BasicConfiguration object.
log4cxx::BasicConfigurator::configure(log4cxx::AppenderPtr(yourAppenderPointer));
this will append your appender to the root logger and works for my case.
Here is my full code snippet of my initialize.
void someclass::initLogger(std::string fileName) {
std::string::size_type found = fileName.find(".log");
std::string strippedFileName;
if (found != std::string::npos)
{
strippedFileName = fileName.substr(0, found);
}
else
{
strippedFileName = fileName;
fileName = fileName + ".log";
}
//initializes for rolling file appenders
rollingFileAppender = new log4cxx::rolling::RollingFileAppender();
rollingPolicy = new log4cxx::rolling::FixedWindowRollingPolicy();
rollingPolicy->setMinIndex(1);
rollingPolicy->setMaxIndex(3);
log4cxx::LogString fileNamePattern = strippedFileName + ".%i.log";
rollingPolicy->setFileNamePattern(fileNamePattern);
trigger = new log4cxx::rolling::SizeBasedTriggeringPolicy();
trigger->setMaxFileSize(1024);
rollingFileAppender->setRollingPolicy(rollingPolicy);
rollingFileAppender->setTriggeringPolicy(trigger);
rollingFileAppender->setLayout(log4cxx::LayoutPtr(new log4cxx::PatternLayout(LOGFILE_LAYOUT_PATTERN)));
rollingFileAppender->setFile(fileName);
rollingFileAppender->setAppend(true);
//initializes for a console appender
consoleAppender = new log4cxx::ConsoleAppender(log4cxx::LayoutPtr(new log4cxx::PatternLayout(LOGFILE_LAYOUT_PATTERN)));
log4cxx::helpers::Pool p;
rollingFileAppender->activateOptions(p);
log4cxx::BasicConfigurator::configure(log4cxx::AppenderPtr(consoleAppender));
log4cxx::BasicConfigurator::configure(log4cxx::AppenderPtr(rollingFileAppender));
}
This code prints to a specified file via a rolling file appender and also prints to the terminal using the consoleAppender
This prints file one file with fileName.log and up to three more with fileName.i.log

Manipulating a QStringList in C++

I have a QStringList named filesToCopy which contains the files name to copy.
I want to make this output:
for %I in ("(", "C:\Users\Nina\Documents\A.mp4",
"C:\Users\Nina\Documents\A.srt", "C:\Users\Nina\Documents\A.txt", ")",
"do copy %I", "C:\Users\Nins\Desktop\z")
to look like this:
for %I in ("C:\Users\Nina\Documents\A.mp4"
"C:\Users\Nina\Documents\A.srt" "C:\Users\Nina\Documents\A.txt") do
copy %I "C:\Users\Nina\Desktop\z"
This is my code:
d->copyProcess = new QProcess(this);
QStringList copyProcessParameters;
Q_FOREACH(QString fileName, fileNames)
{
d->totalFileSize += this->getSize(fileName);
d->filesToCopy.append(fileName);
}
d->filesToCopy.append(")");
d->filesToCopy.prepend("(");
copyProcessParameters.append(d->filesToCopy);
copyProcessParameters.append("do copy %I");
copyProcessParameters.append(destinationDir);
copyProcessParameters.replaceInStrings("/","\\");
qDebug()<<"for %I in" << copyProcessParameters;
d->copyProcess->start("for %I in", copyProcessParameters);
Use QStringList::join() to create the list separated by a space.
And, to make things simple, you can use QString::arg() or its overloads to create the desired string with replacements. That would be more straightforward to use and readable instead of a lot of prepend() and/or append() calls.
Here's an example:
const QString format { R"(for %I in (%1) do copy %I %2)" };
const auto command = format.arg( filesList, destinationDir );
As #Azeem mentioned, you can use join() method:
auto l_files = fileNames.join(", ");
Also note that macro Q_FOREACH is currently (since Qt 5.7) discouraged and will be removed in a future version of Qt. You should use range-based loop from C++ standard:
for(auto& fileName : fileNames)
{
doSomething(fileName);
}
If you want to iterate over const references you can use qAsConst().

How to convert xml node data into string in QT [duplicate]

This question already has an answer here:
Reading an XML file using QXmlStreamReader
(1 answer)
Closed 6 years ago.
I have xml file in my local machine. The xml file format is like:
<string>
<Data>
<Name>Sanket</Name>
<Number>0987654321</Number>
<Address>India</Address>
</Data>
<Data>
<Name>Rahul</Name>
<Number>0987654321</Number>
<Address>Maharashtra</Address>
</Data>
</string>
I want to convert this XML file data into String format. Like:
Sanket 0987654321 India
Rahul 0987654321 Maharashtra
What is the easiest way to convert this data in QT using c++.
I am new in that, so please can anyone suggest me some sample code for this?
Thank you in advance.
I tried following code, but that not work for me:
void parseFile()
{
QList<QList<QString> > dataSet;
QString lastError = "";
QFile inFile("test.xml");
if (inFile.open(QIODevice::ReadOnly))
{
QTextStream fread(&inFile);
long totalSize = inFile.size();
QString line;
while(!fread.atEnd())
{
line = fread.readLine();
QList<QString> record = line.split(QString::KeepEmptyParts);
dataSet.append(record);
}
qDebug()<<dataSet;
}else{
lastError = "Could not open "+test.xml+" for reading";
}
}
You could parse the xml elements firstly via QXmlStreamReader and then you can assemble the xml elements into the string how you want.
The problem of you Code is that you only process the text Lines without any xml-syntax processed by the xml class.
You should look at the QtXML classes for which Florent Uguet provided some links.
However I modified the example found here to do what you want (It does that exact thing for your exact input):
#include <QDomDocument>
#include <QFile>
#include <iostream>
#include <QDomNodeList>
int main()
{
QDomDocument doc("mydocument");
QFile file("test.xml");
if (!file.open(QIODevice::ReadOnly))
return 1;
if (!doc.setContent(&file)) {
file.close();
return 1;
}
file.close();
const auto stringTags = doc.elementsByTagName("string");
for(int stringsI = 0; stringsI < stringTags.size(); ++stringsI){
QDomNode stringTag = stringTags.at(stringsI);
for(QDomNode dataTag = stringTag.firstChildElement("Data"); !dataTag.isNull(); dataTag = dataTag.nextSiblingElement("Data")){
for(QDomNode innerTag = dataTag.firstChild(); !innerTag.isNull(); innerTag = innerTag.nextSibling()){
auto val = innerTag.toElement().text();
std::cout << val.toStdString() << " ";
}
std::cout << std::endl;
}
}
return 0;
}
I build it with QtCreator using qmake. For this you should know that you need to put QT += xml in your *.pro file.
Already asked (and with code) : Reading an XML file using QXmlStreamReader
Qt provides a set of classes for handling XML :
http://doc.qt.io/qt-5.7/qtxml-index.html
http://doc.qt.io/qt-5.7/qxmlstreamreader.html
http://doc.qt.io/qt-5.7/qxmlstreamwriter.html
Old C++ classes (not maintained)
http://doc.qt.io/qt-5/qtxml-module.html
Once you have parsed your file using these, you can usually read the individual nodes' inner text or attributes.

boost log format single attribute with logging::init_from_stream

When I set up format params in code, to format date time output I can use something like this
logging::formatter simpleFormat(expr::format("%1% %2%") %
expr::format_date_time<boost::posix_time::ptime>("TimeStamp", "%H:%M:%S") %
expr::smessage
);
But when I initialize logger with a config file, I can specify format only in attributes position notation, not their format details.
so, this line in a boost log config file
Format="[%TimeStamp%]: %Message%"
produces output:
[2015-Feb-06 09:32:27.401496]: blah blah blah
I want to reduce timestamp to something like this
[06.02.2015 09:32:27]
How can it be described in boost log config file, ot it cant be done at all?
Preamble
My answer is valid for boost 1.55 (haven't tested with latest one). And it was only tested with MSVC 2013 compiler.
Answer
Looks like you need custom formatter_factory for TimeStamp attribute to be able to specify it's format. This works for me:
#include <fstream>
#include "boost/shared_ptr.hpp"
#include "boost/log/trivial.hpp"
#include "boost/log/expressions.hpp"
#include "boost/log/utility/setup.hpp"
#include "boost/log/support/date_time.hpp"
class timestamp_formatter_factory :
public boost::log::basic_formatter_factory<char, boost::posix_time::ptime>
{
public:
formatter_type create_formatter(boost::log::attribute_name const& name, args_map const& args)
{
args_map::const_iterator it = args.find("format");
if (it != args.end())
return boost::log::expressions::stream << boost::log::expressions::format_date_time<boost::posix_time::ptime>(boost::log::expressions::attr<boost::posix_time::ptime>(name), it->second);
else
return boost::log::expressions::stream << boost::log::expressions::attr<boost::posix_time::ptime>(name);
}
};
int main()
{
// Initializing logging
boost::log::register_formatter_factory("TimeStamp", boost::make_shared<timestamp_formatter_factory>());
boost::log::add_common_attributes();
std::ifstream file("settings.ini");
boost::log::init_from_stream(file);
// Testing
BOOST_LOG_TRIVIAL(info) << "Test";
return 0;
}
And now it your settings file you can specify format argument for TimeStamp attribute. Like this:
[Sinks.ConsoleOut]
Destination=Console
AutoFlush=true
Format="[%TimeStamp(format=\"%Y.%m.%d %H:%M:%S\")%]: %Message%"
You should be able to use set_formatter as documented here
sink->set_formatter
(
expr::stream << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
);