Initial commit
This commit is contained in:
commit
2864edb21b
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Project exclude paths
|
||||
/cmake-build-debug/
|
21
CMakeLists.txt
Normal file
21
CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
project(sdp_sniffer_qt)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
set(QT_VERSION 5)
|
||||
set(REQUIRED_LIBS Core Gui Widgets SerialPort)
|
||||
set(REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Gui Qt5::Widgets Qt5::SerialPort)
|
||||
|
||||
file(GLOB_RECURSE SOURCES "src/*.*")
|
||||
include_directories(
|
||||
src
|
||||
)
|
||||
add_executable(${PROJECT_NAME}.elf ${SOURCES})
|
||||
|
||||
find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME}.elf ${REQUIRED_LIBS_QUALIFIED})
|
||||
|
26
sdp-sniffer-qt.pro
Normal file
26
sdp-sniffer-qt.pro
Normal file
@ -0,0 +1,26 @@
|
||||
QT += core gui serialport
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
CONFIG += c++20
|
||||
|
||||
# You can make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
SOURCES += \
|
||||
src/main.cpp \
|
||||
src/MainWindow.cpp \
|
||||
src/MessageDecoder.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/MainWindow.h \
|
||||
src/MessageDecoder.h
|
||||
|
||||
FORMS += \
|
||||
src/MainWindow.ui
|
||||
|
||||
# Default rules for deployment.
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
249
src/MainWindow.cpp
Normal file
249
src/MainWindow.cpp
Normal file
@ -0,0 +1,249 @@
|
||||
#include "MainWindow.h"
|
||||
#include "ui_MainWindow.h"
|
||||
#include <QSerialPortInfo>
|
||||
#include <QMessageBox>
|
||||
#include <QDateTime>
|
||||
#include <fstream>
|
||||
#include <QFileDialog>
|
||||
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
, ui(new Ui::MainWindow) {
|
||||
serial = nullptr;
|
||||
ui->setupUi(this);
|
||||
ui->log->document()->setTextWidth(20);
|
||||
ui->log->setReadOnly(true);
|
||||
setWindowTitle("Podval Sniffer soft");
|
||||
updatePortsList();
|
||||
|
||||
// это перегон текстовика с заранее сохраненными пакетами
|
||||
std::fstream f("/home/vlad/Documents/sniffer-dir/test6.txt", std::ios_base::in | std::ios_base::binary);
|
||||
if (f.good()) {
|
||||
do {
|
||||
char chars[22];
|
||||
f.read((char*) chars, 22);
|
||||
if (f.good()) {
|
||||
for (char c: chars) {
|
||||
printf("%c", c);
|
||||
putCharToMessage(c);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (!f.eof());
|
||||
ui->log->appendHtml("<font style=\"color:green\">[INFO] Файл c данными прочитан. Там " + QString::number(messages.size()) + " записей</font>\n");
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {
|
||||
delete serial;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::on_updatePortsButton_clicked() {
|
||||
updatePortsList();
|
||||
}
|
||||
|
||||
void MainWindow::on_connectButton_clicked() {
|
||||
auto port = ui->portSelect->currentText();
|
||||
if (port.isEmpty()) {
|
||||
ui->log->appendHtml("<font style=\"color:red\">[ERR] Порт не выбран!</font>\n");
|
||||
} else {
|
||||
ui->log->appendHtml("<font style=\"color:green\">[INFO] Попытка открыть порт '" + port + "'</font>\n");
|
||||
if (serial) {
|
||||
serial->close();
|
||||
}
|
||||
serial = new QSerialPort(port);
|
||||
connect(serial, &QSerialPort::readyRead, this, &MainWindow::readDataHandler);
|
||||
|
||||
serial->setBaudRate(500000);
|
||||
serial->setDataBits(QSerialPort::Data8);
|
||||
serial->setFlowControl(QSerialPort::NoFlowControl);
|
||||
serial->setStopBits(QSerialPort::OneStop);
|
||||
|
||||
if (serial->open(QIODevice::ReadWrite)) {
|
||||
ui->log->appendHtml("<font style=\"color:green\">[INFO] Успех!</font>\n");
|
||||
} else {
|
||||
ui->log->appendHtml("<font style=\"color:red\">[ERR] Не удалось открыть порт!</font>\n");
|
||||
QMessageBox::critical(this, tr("Error"), serial->errorString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_disconnect_clicked() {
|
||||
if (serial) {
|
||||
if (serial->isOpen()) {
|
||||
serial->close();
|
||||
}
|
||||
delete serial;
|
||||
serial = nullptr;
|
||||
}
|
||||
ui->log->appendHtml("<font style=\"color:green\">[INFO] Порт <font style=\"color:red\">отключен</font>!</font>\n");
|
||||
}
|
||||
|
||||
void MainWindow::on_clearLog_clicked() {
|
||||
ui->log->clear();
|
||||
messages.clear();
|
||||
}
|
||||
|
||||
static std::string messageStdIdToString(uint16_t stdId) {
|
||||
char groupBuff[64];
|
||||
if ((stdId & 0x7C0) == 0x7C0) {
|
||||
// group = 4;
|
||||
sprintf(groupBuff, "(%03X) Group 4, MsgId=0x%02X ", stdId, (stdId & 0x3F));
|
||||
} else if ((stdId & 0x600) == 0x600) {
|
||||
// group = 3;
|
||||
sprintf(groupBuff, "(%03X) Group 3, MsgId=0x%02X, SrcMAC=0x%02X",
|
||||
stdId, ((stdId & 0x1C0) >> 6), (stdId & 0x3F));
|
||||
} else if ((stdId & 0x600) == 0x400) {
|
||||
// group = 2;
|
||||
sprintf(groupBuff, "(%03X) Group 2, MsgId=0x%02X, MAC-ID=0x%02X",
|
||||
stdId, (stdId & 0x7), ((stdId & 0x1F8) >> 3));
|
||||
} else if ((stdId & 0x400) == 0) {
|
||||
// group = 1;
|
||||
sprintf(groupBuff, "(%03X) Group 1, MsgId=0x%02X, SrcMAC=0x%02X",
|
||||
stdId, ((stdId & 0x3C0) >> 6), (stdId & 0x3F));
|
||||
} else {
|
||||
sprintf(groupBuff, "(%03X) Group is incorrect!", stdId);
|
||||
}
|
||||
return groupBuff;
|
||||
}
|
||||
|
||||
void MainWindow::on_storeToFile_clicked() {
|
||||
QString path = "sniff-" + QDateTime::currentDateTime().toString("yyyy.MM.dd-hh:mm:ss") + ".bin";
|
||||
std::fstream f(path.toStdString(), std::ios_base::out | std::ios_base::binary);
|
||||
if (f.good()) {
|
||||
for (const auto m : messages) {
|
||||
f.write((const char*) &m, sizeof(m));
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
ui->log->appendHtml("<font style=\"color:green\">[INFO] Файл '" + path +
|
||||
"' записан. Там " + QString::number(messages.size()) + " записей</font>\n");
|
||||
}
|
||||
|
||||
void MainWindow::on_loadFromFile_clicked() {
|
||||
messages.clear();
|
||||
auto path = QFileDialog::getOpenFileName(this, "Open Dialog", "", "*.bin");
|
||||
if (!path.isEmpty()) {
|
||||
std::fstream f(path.toStdString(), std::ios_base::in | std::ios_base::binary);
|
||||
if (f.good()) {
|
||||
do {
|
||||
MessageStruct m{};
|
||||
f.read((char*) &m, sizeof(m));
|
||||
if (f.good()) {
|
||||
messages.push_back(m);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (!f.eof());
|
||||
ui->log->appendHtml("<font style=\"color:green\">[INFO] Файл '" + path +
|
||||
"' прочитан. Там " + QString::number(messages.size()) + " записей</font>\n");
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_generateTextFile_clicked() {
|
||||
QString path = "sniff-" + QDateTime::currentDateTime().toString("yyyy.MM.dd-hh:mm:ss") + ".txt";
|
||||
std::fstream f(path.toStdString(), std::ios_base::out);
|
||||
if (f.good()) {
|
||||
for (const auto m : messages) {
|
||||
std::string text = messageToString(m).toStdString() + "\n";
|
||||
f.write(text.c_str(), text.size());
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
ui->log->appendHtml("<font style=\"color:green\">[INFO] Файл '" + path +
|
||||
"' записан. Там " + QString::number(messages.size()) + " записей</font>\n");
|
||||
}
|
||||
|
||||
QString MainWindow::messageToString(const MessageStruct& msg) {
|
||||
char buff[1024];
|
||||
std::string time = QDateTime::fromMSecsSinceEpoch((qint64) msg.milliseconds).
|
||||
toString("yyyy.MM.dd hh:mm:ss.zzz").toStdString();
|
||||
MessageDecoder decoder(msg);
|
||||
sprintf(buff, "%s\n[%s]: %38s, DataLen=%X, Data={%02X %02X %02X %02X %02X %02X %02X %02X}\n",
|
||||
decoder.toString().c_str(), time.c_str(), messageStdIdToString(msg.stdId).c_str(), msg.dataLen,
|
||||
msg.data[0], msg.data[1], msg.data[2], msg.data[3], msg.data[4], msg.data[5], msg.data[6], msg.data[7]);
|
||||
return {buff};
|
||||
}
|
||||
|
||||
static uint8_t charToNum(char data) {
|
||||
if (data >= '0' && data <= '9') return data - '0';
|
||||
else if (data >= 'a' && data <= 'f') return data - 'a' + 10;
|
||||
else if (data >= 'A' && data <= 'F') return data - 'A' + 10;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void MainWindow::putCharToMessage(char data) {
|
||||
if (data == '>') {
|
||||
// начало посылки
|
||||
automat.message.milliseconds = QDateTime::currentMSecsSinceEpoch();
|
||||
automat.currPointer = 0;
|
||||
automat.discard = false;
|
||||
} else if (data == '\n') {
|
||||
// конец посылки
|
||||
if (!automat.discard) {
|
||||
// обработка пакетов
|
||||
if (automat.currPointer != 20) {
|
||||
// ошибка
|
||||
ui->log->appendHtml("[ERR] *** packet crush ***!\n");
|
||||
} else {
|
||||
// вот тут дрочильня с пакетом
|
||||
// сначала парсим StdId
|
||||
automat.message.stdId = charToNum(automat.text[2]) |
|
||||
(charToNum(automat.text[1]) << 4) |
|
||||
(charToNum(automat.text[0]) << 8);
|
||||
|
||||
// теперь длинну посылки
|
||||
automat.message.dataLen = charToNum(automat.text[3]);
|
||||
|
||||
// теперь саму посылку, она все равно всегда передается как 8 байт
|
||||
for (int i = 0; i < 8; i++) {
|
||||
automat.message.data[i] = charToNum(automat.text[i + 5]) |
|
||||
(charToNum(automat.text[i + 4]) << 4);
|
||||
}
|
||||
|
||||
messages.push_back(automat.message);
|
||||
ui->log->appendPlainText(messageToString(automat.message));
|
||||
|
||||
// printf(">%03X%X%02X%02X%02X%02X%02X%02X%02X%02X\n",
|
||||
// automat.message.stdId, automat.message.dataLen,
|
||||
// automat.message.data[0], automat.message.data[1], automat.message.data[2], automat.message.data[3],
|
||||
// automat.message.data[4], automat.message.data[5], automat.message.data[6], automat.message.data[7]);
|
||||
}
|
||||
}
|
||||
|
||||
automat.discard = true; // так, на всякий случай
|
||||
} else {
|
||||
if (!automat.discard) {
|
||||
if (automat.currPointer != 20) {
|
||||
automat.text[automat.currPointer++] = data;
|
||||
} else {
|
||||
automat.discard = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::readDataHandler() {
|
||||
if (serial) {
|
||||
auto array = serial->readAll();
|
||||
for (char i : array) {
|
||||
putCharToMessage(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::updatePortsList() {
|
||||
auto ports = QSerialPortInfo::availablePorts();
|
||||
|
||||
ui->portSelect->clear();
|
||||
for (const auto& p : ports) {
|
||||
ui->portSelect->addItem(p.portName());
|
||||
}
|
||||
}
|
||||
|
49
src/MainWindow.h
Normal file
49
src/MainWindow.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QSerialPort>
|
||||
#include "MessageDecoder.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui { class MainWindow; }
|
||||
QT_END_NAMESPACE
|
||||
|
||||
struct MessageStateAutomat {
|
||||
char text[20]; // SidL0011223344556677
|
||||
int currPointer;
|
||||
bool discard; // отбрасывать сообщение или нет
|
||||
MessageStruct message;
|
||||
};
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MessageStateAutomat automat = {{0}, -1, true, {}};
|
||||
std::vector<MessageStruct> messages;
|
||||
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
|
||||
~MainWindow() override;
|
||||
|
||||
private slots:
|
||||
void on_updatePortsButton_clicked();
|
||||
void on_connectButton_clicked();
|
||||
void on_disconnect_clicked();
|
||||
void on_clearLog_clicked();
|
||||
void on_storeToFile_clicked();
|
||||
void on_loadFromFile_clicked();
|
||||
void on_generateTextFile_clicked();
|
||||
|
||||
private:
|
||||
static QString messageToString(const MessageStruct& msg);
|
||||
void putCharToMessage(char data);
|
||||
void readDataHandler();
|
||||
void updatePortsList();
|
||||
|
||||
Ui::MainWindow *ui;
|
||||
QSerialPort* serial;
|
||||
};
|
||||
#endif // MAINWINDOW_H
|
156
src/MainWindow.ui
Normal file
156
src/MainWindow.ui
Normal file
@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>710</width>
|
||||
<height>328</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Выбрать порт</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="portSelect">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="updatePortsButton">
|
||||
<property name="text">
|
||||
<string>Обновить список</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="connectButton">
|
||||
<property name="text">
|
||||
<string>Соедениться</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="disconnect">
|
||||
<property name="text">
|
||||
<string>Отключиться</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>692</width>
|
||||
<height>206</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="log"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="clearLog">
|
||||
<property name="text">
|
||||
<string>Очистить</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="storeToFile">
|
||||
<property name="text">
|
||||
<string>Сохранить в файл</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="loadFromFile">
|
||||
<property name="text">
|
||||
<string>Загрузить файл</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="generateTextFile">
|
||||
<property name="text">
|
||||
<string>Сгенерить текстовый файл</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>710</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
196
src/MessageDecoder.cpp
Normal file
196
src/MessageDecoder.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
//
|
||||
// Created by vlad on 21.01.2022.
|
||||
//
|
||||
|
||||
#include "MessageDecoder.h"
|
||||
|
||||
MessageDecoder::MessageDecoder(const MessageStruct &message) : msg(message) {}
|
||||
|
||||
int MessageDecoder::getGroup() const {
|
||||
if ((msg.stdId & 0x7C0) == 0x7C0) {
|
||||
// group = 4;
|
||||
return 4;
|
||||
} else if ((msg.stdId & 0x600) == 0x600) {
|
||||
// group = 3;
|
||||
return 3;
|
||||
} else if ((msg.stdId & 0x600) == 0x400) {
|
||||
// group = 2;
|
||||
return 2;
|
||||
} else if ((msg.stdId & 0x400) == 0) {
|
||||
// group = 1;
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int MessageDecoder::getMsgId() const {
|
||||
switch (getGroup()) {
|
||||
case 1: return (msg.stdId & 0x3C0) >> 6;
|
||||
case 2: return msg.stdId & 0x7;
|
||||
case 3: return (msg.stdId & 0x1C0) >> 6;
|
||||
case 4: return msg.stdId & 0x3F;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MessageDecoder::getSrcMac() const {
|
||||
switch (getGroup()) {
|
||||
case 3:
|
||||
case 1: return msg.stdId & 0x3F;
|
||||
case 2:
|
||||
switch (getMsgId()) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 3: return (msg.stdId & 0x1F8) >> 3;
|
||||
case 2:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6: // 6-ой и 7-ой ID кстати не описан в том документе...
|
||||
case 7:
|
||||
// данные должны быть, но не обязаны
|
||||
if (msg.dataLen != 0)
|
||||
return msg.data[0] & 0x3F;
|
||||
else
|
||||
return -1;
|
||||
default: return -1;
|
||||
}
|
||||
case 4:
|
||||
if (msg.dataLen != 0)
|
||||
return msg.data[0] & 0x3F;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int MessageDecoder::getDestMac() const {
|
||||
switch (getGroup()) {
|
||||
case 3:
|
||||
case 1:
|
||||
if (msg.dataLen != 0)
|
||||
return msg.data[0] & 0x3F;
|
||||
else
|
||||
return -1;
|
||||
case 2:
|
||||
switch (getMsgId()) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 3:
|
||||
if (msg.dataLen != 0)
|
||||
return msg.data[0] & 0x3F;
|
||||
else
|
||||
return -1;
|
||||
case 2:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6: // 6-ой и 7-ой ID кстати не описан в том документе...
|
||||
case 7:
|
||||
// данные должны быть, но не обязаны
|
||||
return (msg.stdId & 0x1F8) >> 3;
|
||||
default: return -1;
|
||||
}
|
||||
case 4:
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string MessageDecoder::getMessageDescription() const {
|
||||
switch (getGroup()) {
|
||||
case 1:
|
||||
switch (getMsgId()) {
|
||||
case 0x8: return "Slave I/O Multicast poll response";
|
||||
case 0xC: return "Slave I/O Change of state or cyclic message";
|
||||
case 0xE: return "Slave I/O Bit-Strobe response message";
|
||||
case 0xF: return "Slave I/O Poll response or COS/Cyclic Ack message";
|
||||
default: return "group 2 message";
|
||||
}
|
||||
case 2:
|
||||
switch (getMsgId()) {
|
||||
case 0x0: return "Master I/O Bit-Strobe command message";
|
||||
case 0x1: return "Master I/O Multicast poll Group ID";
|
||||
case 0x2: return "Master Change of state or Cyclic Ack message";
|
||||
case 0x3: return "Slave Explicit/Unconnected response message";
|
||||
case 0x4: return "Master Explicit request message";
|
||||
case 0x5: return "Master I/O Poll command/COS/Cyclic message";
|
||||
// а это уже из таблички из вк, потому что в документе эти сообщения не описаны
|
||||
case 0x6: return "Group 2 only Unconnected explicit request messages";
|
||||
case 0x7: return "Duplicate MAC-ID check message";
|
||||
default: return "Group 2 undefined message";
|
||||
}
|
||||
case 3: return "Group 3 message";
|
||||
case 4: return "Group 4 message";
|
||||
}
|
||||
return "undefined description";
|
||||
}
|
||||
|
||||
static std::string buildDataArray(const MessageStruct& msg) {
|
||||
std::string out = "{";
|
||||
for (int i = 0; i < msg.dataLen; i++) {
|
||||
char tmp[8];
|
||||
if (i == msg.dataLen - 1) {
|
||||
sprintf(tmp, "0x%02X", msg.data[i]);
|
||||
} else {
|
||||
sprintf(tmp, "0x%02X ", msg.data[i]);
|
||||
}
|
||||
out += tmp;
|
||||
}
|
||||
return out + "}";
|
||||
}
|
||||
static std::string buildExplicitMessageFrame(const MessageStruct& msg) {
|
||||
if (msg.dataLen == 0) return "empty explicit frame";
|
||||
char buff[256];
|
||||
sprintf(buff, "{{Frag=%d, XID=%d, MAC=%d}, ",
|
||||
(msg.data[0] & 0x80) != 0, (msg.data[0] & 0x40) != 0, msg.data[0] & 0x3F);
|
||||
|
||||
MessageStruct tmpMsg = msg;
|
||||
if (tmpMsg.dataLen >= 1) {
|
||||
// убираем первый байт, чтобы посылка была
|
||||
tmpMsg.dataLen--;
|
||||
for (int i = 0; i < tmpMsg.dataLen; i++) {
|
||||
tmpMsg.data[i] = tmpMsg.data[i + 1];
|
||||
}
|
||||
}
|
||||
return buff + buildDataArray(tmpMsg) + "}";
|
||||
}
|
||||
static std::string buildBitIOStrobeFrame(const MessageStruct& msg) { return "bit IO strobe frame " + buildDataArray(msg); }
|
||||
static std::string buildChangeOfStateFrame(const MessageStruct& msg) { return "change of state frame " + buildDataArray(msg); }
|
||||
static std::string buildIOPollFrame(const MessageStruct& msg) { return "change of state frame " + buildDataArray(msg); }
|
||||
|
||||
std::string MessageDecoder::getDataFrameDescription() const {
|
||||
if (msg.dataLen == 0) {
|
||||
return "empty frame";
|
||||
} else {
|
||||
switch (getGroup()) {
|
||||
case 1:
|
||||
return "Group 1 frame (IO) " + buildDataArray(msg);
|
||||
case 2:
|
||||
switch (getMsgId()) {
|
||||
case 0: return buildBitIOStrobeFrame(msg);
|
||||
case 1: return buildIOPollFrame(msg);
|
||||
case 2: return buildChangeOfStateFrame(msg);
|
||||
case 3:
|
||||
case 4: return buildExplicitMessageFrame(msg);
|
||||
case 5: return buildIOPollFrame(msg);
|
||||
case 6:
|
||||
case 7: return buildExplicitMessageFrame(msg);
|
||||
}
|
||||
return "Group 2 frame" + buildDataArray(msg);
|
||||
case 3:
|
||||
return buildExplicitMessageFrame(msg);
|
||||
case 4:
|
||||
return "Group 4 frame " + buildDataArray(msg);
|
||||
}
|
||||
}
|
||||
return "unknown frame " + buildDataArray(msg);
|
||||
}
|
||||
|
||||
std::string MessageDecoder::toString() const {
|
||||
char buff[1024];
|
||||
sprintf(buff, "// StdId=0x%03X, DataLen=%d, Group=%d, MsgId=%d, SrcMac=%d, DestMac=%d (%s)\n"
|
||||
"// Frame: %s", msg.stdId, msg.dataLen, getGroup(), getMsgId(), getSrcMac(), getDestMac(),
|
||||
getMessageDescription().c_str(), getDataFrameDescription().c_str());
|
||||
return buff;
|
||||
}
|
44
src/MessageDecoder.h
Normal file
44
src/MessageDecoder.h
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// Created by vlad on 21.01.2022.
|
||||
//
|
||||
|
||||
#ifndef SDP_SNIFFER_QT_MESSAGEDECODER_H
|
||||
#define SDP_SNIFFER_QT_MESSAGEDECODER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
struct MessageStruct {
|
||||
uint16_t stdId;
|
||||
uint8_t dataLen;
|
||||
uint8_t data[8];
|
||||
uint64_t milliseconds;
|
||||
};
|
||||
|
||||
// MAC - все доступные
|
||||
#define MAC_ID_MULTICAST (-1)
|
||||
|
||||
// MAC - не определен
|
||||
#define MAC_ID_UNDEFINED (-2)
|
||||
|
||||
|
||||
class MessageDecoder {
|
||||
private:
|
||||
MessageStruct msg;
|
||||
public:
|
||||
explicit MessageDecoder(const MessageStruct& message);
|
||||
|
||||
int getGroup() const;
|
||||
int getMsgId() const;
|
||||
int getSrcMac() const;
|
||||
int getDestMac() const;
|
||||
std::string getMessageDescription() const;
|
||||
std::string getDataFrameDescription() const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
~MessageDecoder() = default;
|
||||
};
|
||||
|
||||
|
||||
#endif //SDP_SNIFFER_QT_MESSAGEDECODER_H
|
11
src/main.cpp
Normal file
11
src/main.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "MainWindow.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return QApplication::exec();
|
||||
}
|
Reference in New Issue
Block a user