first version of embedded test stand done

This commit is contained in:
2026-05-18 15:21:18 +03:00
commit 6179eb954a
35 changed files with 2042 additions and 0 deletions

42
src/core/cli_parser.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include "cli_parser.h"
#include <QStringList>
TestOptions CLIParser::parse(int argc, char* argv[])
{
TestOptions opt;
QStringList args;
for (int i = 0; i < argc; ++i)
{
args << argv[i];
}
for (int i = 0; i < args.size(); ++i)
{
QString a = args[i];
if (a == "--group" && i + 1 < args.size())
{
opt.group = args[i + 1];
}
else if (a == "--case" && i + 1 < args.size())
{
opt.caseFilter = args[i + 1];
}
else if (a == "--repeat" && i + 1 < args.size())
{
opt.repeat = args[i + 1].toInt();
}
else if (a == "--delay" && i + 1 < args.size())
{
opt.delayMs = args[i + 1].toInt();
}
}
return opt;
}

12
src/core/cli_parser.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef CLI_PARSER_H
#define CLI_PARSER_H
#include "test_options.h"
class CLIParser
{
public:
static TestOptions parse(int argc, char* argv[]);
};
#endif // CLI_PARSER_H

68
src/core/declarations.h Normal file
View File

@@ -0,0 +1,68 @@
#ifndef DECLARATIONS_H
#define DECLARATIONS_H
#include <QByteArray>
#include <QDateTime>
#include <QFile>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include <QHash>
#include <QHashIterator>
#include "QLoggingCategory"
#include "QProcess"
#include <QRegularExpression>
#include <QString>
#include <QStringList>
#include <QSettings>
#include <QTextCodec>
#include <QtCore>
#include <QTextStream>
#include <QtSerialPort/QSerialPort>
#include <QVariant>
#include <QVariantList>
#include <QVector>
#include <gtest/gtest.h>
#include "loggingCategories.h"
#include "html_report.h"
struct comSet_t {
int addrRS485;
QString name;
qint32 baudRate;
QSerialPort::DataBits dataBits;
QSerialPort::Parity parity;
QSerialPort::StopBits stopBits;
QSerialPort::FlowControl flowControl;
comSet_t()
{
addrRS485 = 1;
name = "";
baudRate = QSerialPort::Baud9600;
dataBits = QSerialPort::Data8;
parity = QSerialPort::NoParity;
stopBits = QSerialPort::OneStop;
flowControl = QSerialPort::NoFlowControl;
}
};
typedef comSet_t comSettings_t;
typedef struct {
QString MK_elf_file;
QString MK_map_file;
QString test_log_path;
QString stand_report_html_path;
QString map_parser;
QString elf_parser;
QString iar_json_out;
QString local_python;
} path_t;
#endif // DECLARATIONS_H

View File

@@ -0,0 +1,94 @@
#ifndef EXCEPTIONS_HANDLE_H
#define EXCEPTIONS_HANDLE_H
#include "declarations.h"
using namespace std;
/*!
* \brief the ErrOpenFile class for handling exceptions when opening file
*/
class ErrOpenFile: public exception
{
public:
ErrOpenFile(QString message, QString filePath)
{
this->message = message;
this->filePath = filePath;
}
QString getFilePath()
{ return filePath; }
QString getMessage()
{ return message; }
virtual ~ErrOpenFile() throw() {}
private:
QString message;
QString filePath;
};
//class ErrOpenFile: public exception
//{
//public:
// ErrOpenFile(QString fname, QString errMsg)
// {
// this->fname = fname;
// this->intro = "ошибка чтения файла";
// this->errMsg = errMsg;
// }
// QString getFname() { return fname; }
// QString getIntro() { return intro; }
// QString getErrMsg() { return errMsg; }
// virtual ~ErrOpenFile() throw() {}
//private:
// QString fname;
// QString intro;
// QString errMsg;
//};
class ErrInJsonSet: public exception
{
public:
ErrInJsonSet(QString fname, QString jsonObjectsList, QString param, QString errMsg)
{
this->fname = fname;
this->jsonObjectsList = jsonObjectsList;
this->param = param;
this->intro = "ошибка чтения файла настроек";
this->errMsg = errMsg;
this->errFromJsonFlag = false;
}
ErrInJsonSet(QString fname, QString errFromJson)
{
this->fname = fname;
this->errFromJsonFlag = true;
this->intro = "ошибка чтения файла настроек";
this->errFromJson = errFromJson;
}
QString getFname() { return fname; }
QString getJsonObj() { return jsonObjectsList; }
QString getParam() { return param; }
QString getIntro() { return intro; }
QString getErrMsg() { return errMsg; }
QString getErrFromJson() { return errFromJson; }
bool checkErrFromJson() { return errFromJsonFlag; }
virtual ~ErrInJsonSet() throw() {}
private:
QString fname;
QString intro;
QString jsonObjectsList;
QString param;
QString errMsg;
QString errFromJson;
bool errFromJsonFlag = false;
};
#endif // EXCEPTIONS_HANDLE_H

158
src/core/html_report.cpp Normal file
View File

@@ -0,0 +1,158 @@
#include "html_report.h"
#include <QFile>
#include <QStringList>
#include <QTextStream>
static QMap<QString,int> extractSummary(
const QString& content)
{
QMap<QString,int> s;
QStringList lines =
content.split("\n");
for (QString line : lines)
{
if (line.startsWith("TOTAL:"))
s["total"] =
line.split(":")[1].trimmed().toInt();
if (line.startsWith("PASSED:"))
s["passed"] =
line.split(":")[1].trimmed().toInt();
if (line.startsWith("FAILED:"))
s["failed"] =
line.split(":")[1].trimmed().toInt();
}
return s;
}
void HtmlReport::generate(
const QString& logFile,
const QString& htmlFile,
QString curTime)
{
QFile f(logFile);
f.open(QIODevice::ReadOnly);
QString content =
f.readAll();
f.close();
auto summary =
extractSummary(content);
QString html;
html += R"(
<html>
<head>
<style>
body {
font-family: monospace;
background:#111;
color:#eee;
}
.pass {
color:lightgreen;
}
.fail {
color:red;
}
details {
margin-bottom:10px;
}
summary {
cursor:pointer;
font-weight:bold;
}
pre {
white-space:pre-wrap;
}
</style>
</head>
<body>
)";
html += QString("<h1>Tests Report (%1)</h1>").arg(curTime);
html += QString(R"(
<h2>SUMMARY</h2>
<div>
<b>Total:</b> %1<br>
<b style='color:lightgreen;'>Passed:</b> %2<br>
<b style='color:red;'>Failed:</b> %3<br>
</div>
)")
.arg(summary["total"])
.arg(summary["passed"])
.arg(summary["failed"]);
QStringList tests =
content.split("<<<TEST_START>>>");
for (QString t : tests)
{
if (!t.contains("TEST:"))
continue;
QString cls =
t.contains("STATUS: FAIL")
? "fail"
: "pass";
QString title =
t.split("\n")[1];
html += QString(R"(
<details>
<summary class="%1">%2</summary>
<pre>%3</pre>
</details>
)")
.arg(cls)
.arg(title)
.arg(t.toHtmlEscaped());
}
int passRate = 0;
if (summary["total"] > 0)
{
passRate =
(100 * summary["passed"])
/ summary["total"];
}
html += QString(R"(
<div style="background:#333;width:300px;">
<div style="
background:green;
width:%1%;
color:white;">
%1%
</div>
</div>
)").arg(passRate);
html += "</body></html>";
QFile out(htmlFile);
out.open(QIODevice::WriteOnly);
out.write(html.toUtf8());
out.close();
}

14
src/core/html_report.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef HTML_REPORT_H
#define HTML_REPORT_H
#include <QString>
class HtmlReport
{
public:
static void generate(const QString& logFile,
const QString& htmlFile, QString curTime);
};
#endif // HTML_REPORT_H

182
src/core/json_processor.cpp Normal file
View File

@@ -0,0 +1,182 @@
#include "json_processor.h"
JsonProcessor::JsonProcessor()
{
}
void JsonProcessor::setJsonFile(QString jsonPath_)
{
jsonPath = jsonPath_;
}
void JsonProcessor::openJsonFile(QString jsonPath, QJsonObject &jsonObj)
{
QByteArray bytes;
QFile file(jsonPath);
if (file.open( QIODevice::ReadOnly))
{
bytes = file.readAll();
file.close();
}
else
{ throw(ErrOpenFile(jsonPath, "файл не найден")); }
QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonError);
if (jsonError.error != QJsonParseError::NoError)
{ throw(ErrInJsonSet(jsonPath, jsonError.errorString())); return; }
else
{
if (doc.isObject())
{ jsonObj = doc.object(); }
else
{ throw(ErrInJsonSet(jsonPath, "неверный формат json: " + jsonPath)); }
}
}
void JsonProcessor::saveJsonDataInFile(QString jsonPath, QJsonObject jsonObj)
{
QJsonDocument document;
document.setObject(jsonObj);
QByteArray bytes = document.toJson( QJsonDocument::Indented );
QFile file(jsonPath);
if( file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
{
QTextStream iStream( &file );
iStream.setCodec( "utf-8" );
iStream << bytes;
file.close();
}
else
{ throw(ErrOpenFile(jsonPath, "файл не найден")); }
}
/* common functions */
void JsonProcessor::jsonConvertStruct(QString jsonObjName, QJsonObject obj,
QString jsonParamName, QVector<QString> *setToStructArr, int arrSize)
{
setToStructArr->clear();
QJsonArray *param = new QJsonArray;
*param = obj[jsonParamName].toArray();
if (param->size() != arrSize)
{ throw(ErrInJsonSet(jsonPath, jsonObjName, jsonParamName, "параметр отсутствует или кол-во элементов в параметре должно быть равно " + QString::number(arrSize))); }
else
{
for (int i = 0; i < arrSize; i++)
{ setToStructArr->push_back(param->at(i).toString()); }
}
}
void JsonProcessor::jsonConvertStruct(QJsonObject obj, QString jsonParamName,
QVector<QString> *setToStructArr)
{
setToStructArr->clear();
QJsonArray *param = new QJsonArray;
*param = obj[jsonParamName].toArray();
for (int i = 0; i < param->size(); i++)
{ setToStructArr->push_back(param->at(i).toString()); }
}
void JsonProcessor::jsonConvertStruct(QJsonObject obj,
QString jsonParamName, QVector<double> *setToStructArr)
{
setToStructArr->clear();
QJsonArray *param = new QJsonArray;
*param = obj[jsonParamName].toArray();
for (int i = 0; i < param->size(); i++)
{ setToStructArr->push_back(param->at(i).toDouble()); }
}
void JsonProcessor::jsonConvertStruct(QString jsonObjName, QJsonObject obj,
QString jsonParamName, double *setToStructArr, int arrSize)
{
QJsonArray param = obj[jsonParamName].toArray();
if (param.size() != arrSize)
{ throw(ErrInJsonSet(jsonPath, jsonObjName, jsonParamName,
"параметр отсутствует или кол-во элементов в параметре должно быть равно " + QString::number(arrSize))); }
else
{
for (int i = 0; i < arrSize; i++)
{ setToStructArr[i] = param[i].toDouble(); }
}
}
void JsonProcessor::jsonConvertStruct(QString jsonObjName, QJsonObject obj,
QString jsonParamName, bool *setToStructArr, int arrSize)
{
QJsonArray param = obj[jsonParamName].toArray();
if (param.size() != arrSize)
{ throw(ErrInJsonSet(jsonPath, jsonObjName, jsonParamName,
"параметр отсутствует или кол-во элементов в параметре должно быть равно " + QString::number(arrSize))); }
else
{
for (int i = 0; i < arrSize; i++)
{ setToStructArr[i] = param[i].toBool(); }
}
}
bool JsonProcessor::jsonGetBoolValue(QJsonObject obj, QString param, QString jsonGeneral)
{
QJsonValue val = obj[param];
if (val == QJsonValue::Null)
{ throw(ErrInJsonSet(jsonPath, jsonGeneral, param, "отсутствует параметр")); }
return val.toBool(false);
}
void JsonProcessor::jsonGetStrValue(QJsonObject obj, QString paramName, QString &paramValue, QString jsonObjName)
{
QString val = obj[paramName].toString();
if (val == NULL)
{ throw(ErrInJsonSet(jsonPath, jsonObjName, paramName, "parameter missing")); }
else
{ paramValue = val; }
}
void JsonProcessor::jsonSetComPortSettings(QString jsonObjName, QJsonObject obj, comSettings_t &com)
{
com.addrRS485 = obj["RS485_address"].toInt();
if (com.addrRS485 < 1 ||
com.addrRS485 > 254)
{ throw(ErrInJsonSet(jsonPath, jsonObjName, "RS485_address", "отсутствует параметр")); }
com.name = obj["COM_port"].toString();
if (com.name == NULL) { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_port", "отсутствует параметр")); }
com.baudRate = (quint32)obj["COM_baudRate"].toInt();
if (com.baudRate == 0) { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_baudRate", "отсутствует параметр")); }
com.dataBits = (QSerialPort::DataBits)obj["COM_bits"].toInt();
if (com.dataBits < QSerialPort::Data5 ||
com.dataBits > QSerialPort::Data8)
{ throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_bits", "отсутствует параметр")); }
com.parity = (QSerialPort::Parity)obj["COM_parity"].toInt();
if (com.parity < QSerialPort::UnknownParity ||
com.parity == 1 ||
com.parity > QSerialPort::MarkParity)
{ throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_parity", "отсутствует параметр")); }
com.stopBits = (QSerialPort::StopBits)obj["COM_stopBits"].toInt();
if (com.stopBits < QSerialPort::OneStop ||
com.stopBits > QSerialPort::OneAndHalfStop)
{ throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_stopBits", "отсутствует параметр")); }
com.flowControl = (QSerialPort::FlowControl)obj["COM_flowCotrol"].toInt();
if (com.flowControl < QSerialPort::NoFlowControl ||
com.flowControl > QSerialPort::SoftwareControl)
{ throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_flowControl", "отсутствует параметр")); }
}
void JsonProcessor::jsonSaveComPortSettings(QJsonObject &obj, comSettings_t &com)
{
obj.insert("RS485_address", com.addrRS485);
obj.insert("COM_port", com.name);
obj.insert("COM_baudRate", com.baudRate);
obj.insert("COM_bits", com.dataBits);
obj.insert("COM_parity", com.parity);
obj.insert("COM_stopBits", com.stopBits);
obj.insert("COM_flowControl", com.flowControl);
}

39
src/core/json_processor.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef JSONPROCESSOR_H
#define JSONPROCESSOR_H
#include "exceptions_handle.h"
class JsonProcessor
{
public:
JsonProcessor();
virtual void setJsonFile(QString jsonPath_);
virtual void openJsonFile(QString jsonPath, QJsonObject &jsonObj);
virtual void saveJsonDataInFile(QString jsonPath, QJsonObject jsonObj);
virtual void jsonConvertStruct(QString jsonObjName, QJsonObject obj,
QString jsonParamName, QVector<QString> *setToStructArr, int arrSize);
virtual void jsonConvertStruct(QJsonObject obj,
QString jsonParamName, QVector<QString> *setToStructArr);
virtual void jsonConvertStruct(QJsonObject obj,
QString jsonParamName, QVector<double> *setToStructArr);
virtual void jsonConvertStruct(QString jsonObjName, QJsonObject obj,
QString jsonParamName, double *setToStructArr, int arrSize);
virtual void jsonConvertStruct(QString jsonObjName, QJsonObject obj,
QString jsonParamName, bool *setToStructArr, int arrSize);
virtual bool jsonGetBoolValue(QJsonObject obj, QString param, QString jsonPSgeneral);
virtual void jsonSetComPortSettings(QString jsonObjName, QJsonObject obj, comSettings_t &com);
virtual void jsonSaveComPortSettings(QJsonObject &obj, comSettings_t &com);
virtual void jsonGetStrValue(QJsonObject obj, QString paramName, QString &paramValue, QString jsonObjName);
private:
QString jsonPath = "";
};
#endif // JSONPROCESSOR_H

127
src/core/logger.cpp Normal file
View File

@@ -0,0 +1,127 @@
#include "logger.h"
// Умный указатель на файл логирования
QScopedPointer<QFile> m_logFile;
void Logger::write(const QString& file, const QString& text)
{
QFile f(file);
f.open(QIODevice::Append | QIODevice::Text);
QTextStream out(&f);
out << text;
f.close();
}
void Logger::saveTestLog(
const QString& filename,
const ProtocolHandler& handler,
const QJsonObject& cfg,
bool passed,
const QString& error)
{
QString log;
log += "\n<<<TEST_START>>>\n";
log += "TEST: ";
log += cfg["meta"].toObject()["name"].toString();
log += "\n";
log += "TIME: ";
log += QDateTime::currentDateTime().toString("dd.MM.yyyy hh:mm:ss");
log += "\n";
log += passed ? "STATUS: PASS\n" : "STATUS: FAIL\n";
if (!error.isEmpty())
{
log += "\n--- ERROR ---\n";
log += error;
log += "\n";
}
if (!handler.mismatches.isEmpty())
{
log += "\n--- MISMATCHES ---\n";
QJsonDocument doc(QJsonArray::fromVariantList(handler.mismatches));
log += doc.toJson();
log += "\n";
}
log += "\n--- FULL LOG ---\n";
for (const QString& s : handler.messages)
{
log += s;
log += "\n";
}
log += "<<<TEST_END>>>\n";
write(filename, log);
}
void Logger::appendSummary(const QString& filename, int total,
int passed, int failed)
{
QString s;
s += "\n";
s += "############################################################\n";
s += "SUMMARY\n";
s += "############################################################\n";
s += QString("TOTAL: %1\n").arg(total);
s += QString("PASSED: %1\n").arg(passed);
s += QString("FAILED: %1\n").arg(failed);
write(filename, s);
}
void Logger::setupLog()
{
// Устанавливаем файл логирования
m_logFile.reset(new QFile("C:/Danila/work/embedded_test_stand/logs/test_stand.log"));
// Открываем файл логирования
if (!m_logFile.data()->open(QFile::Append | QFile::Text))
{ printf("\nlog file is not opened!\n"); }
// Устанавливаем обработчик
qInstallMessageHandler(writeStandLog);
}
void Logger::writeToConsol(QString msg)
{
QTextStream out(stdout);
out.setCodec("IBM 866");
out << msg << Qt::endl;
}
void Logger::writeStandLog(QtMsgType type,
const QMessageLogContext &context,
const QString &msg)
{
// Открываем поток записи в файл
QTextStream out(m_logFile.data());
// Записываем дату записи
out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ");
// По типу определяем, к какому уровню относится сообщение
switch (type)
{
case QtInfoMsg: out << "INF "; break;
case QtDebugMsg: out << "DBG "; break;
case QtWarningMsg: out << "WRN "; break;
case QtCriticalMsg: out << "CRT "; break;
case QtFatalMsg: out << "FTL "; break;
}
// Записываем в вывод категорию сообщения и само сообщение
out << context.category << ": " << msg << Qt::endl;
out.flush(); // Очищаем буферизированные данные
}

39
src/core/logger.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef LOGGER_H
#define LOGGER_H
#include "protocol_handler.h"
class ProtocolHandler;
class QJsonObject;
class Logger
{
public:
static void saveTestLog(
const QString& filename,
const ProtocolHandler& handler,
const QJsonObject& cfg,
bool passed,
const QString& error = "");
static void appendSummary(
const QString& filename,
int total,
int passed,
int failed);
static void setupLog();
static void writeStandLog(QtMsgType type, const QMessageLogContext &context,
const QString &msg);
static void writeToConsol(QString msg);
private:
static void write(
const QString& file,
const QString& text);
};
#endif // LOGGER_H

View File

@@ -0,0 +1,6 @@
#include "loggingCategories.h"
Q_LOGGING_CATEGORY(logDebug, "Debug")
Q_LOGGING_CATEGORY(logInfo, "Info")
Q_LOGGING_CATEGORY(logWarning, "Warning")
Q_LOGGING_CATEGORY(logCritical, "Critical")

View File

@@ -0,0 +1,11 @@
#ifndef LOGGINGCATEGORIES_H
#define LOGGINGCATEGORIES_H
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(logDebug)
Q_DECLARE_LOGGING_CATEGORY(logInfo)
Q_DECLARE_LOGGING_CATEGORY(logWarning)
Q_DECLARE_LOGGING_CATEGORY(logCritical)
#endif // LOGGINGCATEGORIES_H

View File

@@ -0,0 +1,194 @@
#include "protocol_handler.h"
ProtocolHandler::ProtocolHandler(const QJsonObject& expectations)
{
exp = expectations;
}
void ProtocolHandler::feed(const QByteArray& data)
{
QString text = QString::fromUtf8(data);
buffer += text;
messages.push_back(text);
QJsonValue startExp = exp["start"];
QJsonValue endExp = exp["end"];
if (startExp.isNull())
{ startReceived = true; }
if (!startReceived)
{ checkStart(); }
if (startReceived)
{ checkSequence(); }
if (endExp.isNull())
{ endReceived = true; }
if (!endReceived)
{ checkEnd(); }
}
void ProtocolHandler::checkStart()
{
if (!buffer.contains("{"))
return;
int start = buffer.indexOf("{");
int end = buffer.indexOf("}");
if (end < 0)
return;
QString jsonText = buffer.mid(start, end - start + 1);
QJsonDocument doc = QJsonDocument::fromJson(jsonText.toUtf8());
if (!doc.isObject()) return;
QJsonObject msg = doc.object();
QJsonObject expected = exp["start"]
.toObject()["fields"]
.toObject();
bool ok = true;
for (QString key : expected.keys())
{
if (msg[key] != expected[key])
{
ok = false;
break;
}
}
if (ok)
{ startReceived = true; }
else
{
QVariantMap m;
m["stage"] = "start";
m["expected"] = expected;
m["actual"] = msg.toVariantMap();
mismatches.append(m);
}
buffer = buffer.mid(end + 1);
}
void ProtocolHandler::checkSequence()
{
QJsonArray seq = exp["sequence"].toArray();
while (sequenceIndex < seq.size())
{
QJsonObject current =
seq[sequenceIndex].toObject();
QString type = current["type"].toString();
QString value = current["value"].toString();
bool matched = false;
if (type == "contains")
{
matched = buffer.contains(value);
}
else if (type == "regex")
{
QRegularExpression re(value);
matched = re.match(buffer).hasMatch();
}
if (matched)
{
sequenceIndex++;
}
}
}
void ProtocolHandler::checkEnd()
{
QJsonObject endExp = exp["end"].toObject();
if (endExp.isEmpty()) return;
QString type = endExp["type"].toString();
QString value = endExp["value"].toString();
if (type == "contains")
{
if (buffer.contains(value))
{
endReceived = true;
// sequence incomplete
QJsonArray seq = exp["sequence"].toArray();
if (sequenceIndex < seq.size())
{
QVariantMap m;
m["stage"] = "sequence";
m["step"] = sequenceIndex;
m["expected"] =
seq[sequenceIndex].toObject()
.toVariantMap()["value"];
m["actual_buffer"] = buffer.right(200);
mismatches.append(m);
}
}
}
}
bool ProtocolHandler::isDone(QString &msg) const
{
bool startOk = true;
bool endOk = true;
QJsonValue startExp = exp["start"];
QJsonValue endExp = exp["end"];
if (!startExp.isNull())
{
startOk = startReceived;
}
if (!endExp.isNull())
{
endOk = endReceived;
}
if (!startOk)
{
msg.append("\nstart sequence did not receive");
}
if (!endOk)
{
msg.append("\nend sequence did not receive");
}
int seqSize =
exp["sequence"]
.toArray()
.size();
return (
startOk
&& sequenceIndex >= seqSize
&& endOk
);
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include "declarations.h"
class ProtocolHandler
{
public:
ProtocolHandler(const QJsonObject& expectations);
void feed(const QByteArray& data);
bool isDone(QString &msg) const;
QStringList messages;
QVariantList mismatches;
private:
void checkStart();
void checkSequence();
void checkEnd();
private:
QJsonObject exp;
QString buffer;
bool startReceived = false;
bool endReceived = false;
int sequenceIndex = 0;
};

47
src/core/test_loader.cpp Normal file
View File

@@ -0,0 +1,47 @@
#include "test_loader.h"
QStringList TestLoader::loadCases(const QString& group,
const QString& caseFilter,
int repeat)
{
QString fname = "C:/Danila/work/embedded_test_stand/configs/test_plan.json";
QFile f(fname);
QStringList result;
if (!f.open(QIODevice::ReadOnly))
{
throw(ErrOpenFile("cannot open json file", fname));
}
QJsonObject root = QJsonDocument::fromJson(f.readAll()).object();
const QJsonArray arr = root["groups"].toObject()[group].toArray();
if (arr.count() == 0)
{
throw(ErrOpenFile("invalid format in json file!", fname));
}
for (auto v : arr)
{
QString name = v.toString();
// skip test
if (name.startsWith("#"))
continue;
// filter
if (!caseFilter.isEmpty())
{
if (!name.contains(caseFilter))
continue;
}
for (int i = 0; i < repeat; ++i)
{ result << QString("C:/Danila/work/embedded_test_stand/configs/test_cases/%1").arg(name); }
}
return result;
}

11
src/core/test_loader.h Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
#include "exceptions_handle.h"
class TestLoader
{
public:
static QStringList loadCases(const QString& group,
const QString& caseFilter = QString(),
int repeat = 1);
};

View File

@@ -0,0 +1 @@
#include "test_options.h"

17
src/core/test_options.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef TEST_OPTIONS_H
#define TEST_OPTIONS_H
#include <QString>
struct TestOptions
{
QString group = "full";
QString caseFilter;
int repeat = 1;
int delayMs = 0;
};
#endif // TEST_OPTIONS_H

12
src/core/test_stats.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef TEST_STATS_H
#define TEST_STATS_H
struct TestStats
{
int total = 0;
int passed = 0;
int failed = 0;
};
#endif // TEST_STATS_H

59
src/core/uart.cpp Normal file
View File

@@ -0,0 +1,59 @@
#include "uart.h"
UART::UART()
{
}
UART::~UART()
{
}
void UART::connect(comSettings_t set)
{
serial.setPortName(set.name);
serial.setBaudRate(set.baudRate);
serial.setDataBits(set.dataBits);
serial.setParity(set.parity);
serial.setStopBits(set.stopBits);
serial.setFlowControl(set.flowControl);
if (!serial.open(QIODevice::ReadWrite))
{
throw(ErrOpenFile(QString("error opening com-port: %1").
arg(serial.errorString()), set.name));
}
}
bool UART::send(const QByteArray& data, QString &err)
{
bool st = false;
int status = serial.write(data);
serial.flush();
if (status == -1)
{ return false; }
else
{ st = serial.waitForBytesWritten(3000); }
err = serial.errorString();
return st;
}
QByteArray UART::receive(int timeoutMs)
{
QByteArray result;
while (serial.waitForReadyRead(timeoutMs))
{
result += serial.readAll();
}
return result;
}
void UART::close()
{
serial.close();
}

22
src/core/uart.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include "exceptions_handle.h"
class UART
{
public:
UART();
~UART();
public:
void connect(comSettings_t set);
bool send(const QByteArray& data, QString &err);
QByteArray receive(int timeoutMs);
void close();
private:
QSerialPort serial;
};