1.添加上位机工具。

This commit is contained in:
2024-05-21 21:09:55 +08:00
parent ce33c00574
commit cb451b3070
16 changed files with 1090 additions and 3 deletions

61
Analyser/Analyser.rc Normal file
View File

@ -0,0 +1,61 @@
// Microsoft Visual C++ generated resource script.
//
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#pragma code_page(936)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
2 TEXTINCLUDE BEGIN "#include "
"winres.h"
"\r\n"
"\0" END
3 TEXTINCLUDE BEGIN "\r\n"
"\0" END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1 ICON "..\\resources\\logo.ico"
#endif // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

20
Analyser/CMakeLists.txt Normal file
View File

@ -0,0 +1,20 @@
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets SerialPort)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets SerialPort)
add_executable(Analyser Analyser.rc
main.cpp
CategoryLogSinkBackend.h CategoryLogSinkBackend.cpp
Widget.h Widget.cpp
ModuleCommunication.h ModuleCommunication.cpp
)
target_link_libraries(Analyser
PRIVATE Universal
PRIVATE QtComponets
PRIVATE Ws2_32
PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
PRIVATE Qt${QT_VERSION_MAJOR}::SerialPort
)

View File

@ -0,0 +1,15 @@
#include "CategoryLogSinkBackend.h"
#include "AsyncEvent.h"
#include <QTextBrowser>
#include <iostream>
CategoryLogSinkBackend::CategoryLogSinkBackend(const std::string &category, QTextBrowser *target)
: m_category(category), m_target(target) {
}
void CategoryLogSinkBackend::consume(const boost::log::record_view &record, string_type const &output) {
auto &&category = record[AmassKeywords::category];
if (category == m_category) {
Amass::executeAtObjectThread(m_target, [this, output]() { m_target->append(QString::fromStdString(output)); });
}
}

View File

@ -0,0 +1,20 @@
#ifndef __CATEGORYLOGSINKBACKEND_H__
#define __CATEGORYLOGSINKBACKEND_H__
#include <boost/log/sinks.hpp>
#include <string>
class QTextBrowser;
class CategoryLogSinkBackend
: public boost::log::sinks::basic_formatted_sink_backend<char, boost::log::sinks::synchronized_feeding> {
public:
CategoryLogSinkBackend(const std::string &category, QTextBrowser *target);
void consume(const boost::log::record_view &record, string_type const &output);
private:
std::string m_category;
QTextBrowser *m_target = nullptr;
};
#endif // __CATEGORYLOGSINKBACKEND_H__

View File

@ -0,0 +1,204 @@
#include "ModuleCommunication.h"
#include "BoostLog.h"
#include <QSerialPort>
#include <WinSock2.h>
#include <sstream>
static inline uint8_t xor_checksum_byte(const uint8_t *data, uint32_t len) {
uint8_t sum = 0;
for (uint32_t i = 0; i < len; ++i) {
sum ^= data[i];
}
return sum;
}
ModuleCommunication::ModuleCommunication(QObject *parent) : QObject{parent} {
}
bool ModuleCommunication::open(const QString &portName) {
bool ret = true;
m_serialPort = std::make_shared<QSerialPort>(portName);
m_serialPort->setBaudRate(QSerialPort::Baud115200);
connect(m_serialPort.get(), &QSerialPort::readyRead, this, &ModuleCommunication::onReadyRead);
ret = m_serialPort->open(QSerialPort::ReadWrite);
LOG_CAT(info, GUI) << "打开串口(" << portName.toStdString() << ")" << (ret ? "成功" : "失败") << "";
LOG_CAT(info, GUI) << Separator;
return ret;
}
void ModuleCommunication::verify(uint8_t timeout) {
VerifyInfo data = {0};
data.timeout = timeout;
auto [frameData, frameSize] = generateFrame(Verify, reinterpret_cast<const uint8_t *>(&data), sizeof(data));
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
LOG_CAT(info, GUI) << "发送识别指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << Separator;
}
void ModuleCommunication::enroll(const std::string &username, uint8_t timeout) {
EnrollData data = {0};
data.timeout = timeout;
strncpy(reinterpret_cast<char *>(data.username), username.c_str(), sizeof(data.username));
auto [frameData, frameSize] = generateFrame(EnrollSingle, reinterpret_cast<const uint8_t *>(&data), sizeof(data));
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
LOG_CAT(info, GUI) << "发送注册指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << "用户名: " << username << ", 超时时间: " << static_cast<int>(timeout) << "s";
LOG_CAT(info, GUI) << Separator;
}
void ModuleCommunication::deleteUser(uint16_t userid) {
uint16_t n = htons(userid);
auto [frameData, frameSize] = generateFrame(DeleteUser, reinterpret_cast<const uint8_t *>(&n), sizeof(n));
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
LOG_CAT(info, GUI) << "发送删除用户指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << "删除用户ID: " << userid;
LOG_CAT(info, GUI) << Separator;
}
void ModuleCommunication::deleteAll() {
auto [frameData, frameSize] = generateFrame(DeleteAll);
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
LOG_CAT(info, GUI) << "发送删除所有指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << Separator;
}
void ModuleCommunication::onReadyRead() {
auto data = m_serialPort->readAll();
if (data.size() < 6) {
LOG(warning) << "invalid data: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()), data.size());
return;
}
uint8_t messageId = data[2];
switch (messageId) {
case Reply: {
uint8_t replyId = data[5];
auto result = data[6];
switch (replyId) {
case Verify: {
if (result == Success) {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
auto info = reinterpret_cast<VerifyDataReply *>(data.data() + 7);
LOG_CAT(info, GUI) << "用户ID: " << ntohs(info->userid)
<< ", 用户名: " << std::string_view(reinterpret_cast<const char *>(info->username));
LOG_CAT(info, GUI) << Separator;
} else if (result == Failed4Timeout) {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
LOG_CAT(info, GUI) << "识别超时。";
LOG_CAT(info, GUI) << Separator;
} else if (result == Failed4UnknownReason) {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
LOG_CAT(info, GUI) << "未录入用户。";
LOG_CAT(info, GUI) << Separator;
} else {
}
break;
}
case EnrollSingle: {
if (result == Success) {
auto info = reinterpret_cast<EnrollDataReply *>(data.data() + 7);
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
LOG_CAT(info, GUI) << "注册成功,用户ID: " << ntohs(info->userid);
LOG_CAT(info, GUI) << Separator;
}
break;
}
case DeleteUser: {
if (result == Success) {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
LOG_CAT(info, GUI) << "删除用户成功。";
LOG_CAT(info, GUI) << Separator;
} else {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
LOG_CAT(info, GUI) << "删除用户失败。";
LOG_CAT(info, GUI) << Separator;
}
break;
}
case DeleteAll: {
if (result == Success) {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
LOG_CAT(info, GUI) << "删除所有用户成功。";
LOG_CAT(info, GUI) << Separator;
}
break;
}
default:
LOG(warning) << "unknown reply command: 0x" << (static_cast<int>(replyId) & 0xff) << ", data: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()), data.size());
break;
}
break;
}
case Note: {
uint8_t noteId = data[5];
switch (noteId) {
case Ready: {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()), data.size());
LOG_CAT(info, GUI) << "模组上电初始化成功。";
LOG_CAT(info, GUI) << Separator;
break;
}
case 0x01: { // 模组返回的数据为当前帧的手掌状态
auto info = reinterpret_cast<VerifyNoteInfo *>(data.data() + 7);
LOG(info) << info->state;
break;
}
default:
LOG(warning) << "unknown note command: 0x" << (static_cast<int>(noteId) & 0xff) << ", data: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()), data.size());
break;
}
break;
}
default:
LOG(warning) << "unknown command: 0x" << (static_cast<int>(data[2]) & 0xff) << ", data: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()), data.size());
break;
}
}
std::pair<uint8_t *, uint32_t> ModuleCommunication::generateFrame(MessageId command, const uint8_t *data,
uint16_t size) {
static uint8_t sendBuffer[1024] = {0};
memset(sendBuffer, 0, sizeof(sendBuffer));
sendBuffer[0] = 0xef;
sendBuffer[1] = 0xaa;
sendBuffer[2] = command;
sendBuffer[3] = size >> 8;
sendBuffer[4] = size & 0xff;
if (size > 0) {
memcpy(sendBuffer + 5, data, size);
}
sendBuffer[5 + size] = xor_checksum_byte(sendBuffer + 2, 3 + size);
return std::make_pair(sendBuffer, size + 6);
}
std::string ModuleCommunication::protocolDataFormatString(const uint8_t *data, int size) {
std::ostringstream oss;
for (int i = 0; i < size; i++) {
oss << "0x" << std::hex << (static_cast<int>(data[i]) & 0xff) << " ";
}
return oss.str();
}

View File

@ -0,0 +1,100 @@
#ifndef MODULECOMMUNICATION_H
#define MODULECOMMUNICATION_H
#include <QObject>
#include <memory>
class QSerialPort;
class ModuleCommunication : public QObject {
Q_OBJECT
static constexpr uint32_t UsernameSize = 32;
static constexpr const char *Separator = "----------";
public:
enum MessageId : uint8_t {
Reply = 0,
Note = 0x01,
Verify = 0x12,
EnrollSingle = 0x1D,
DeleteUser = 0x20,
DeleteAll = 0x21,
};
enum NoteId : uint8_t {
Ready = 0x00,
};
enum MessageStatus : uint8_t {
Success = 0,
Rejected = 1,
Aborted = 2,
Failed4Camera = 4,
Failed4UnknownReason = 5,
Failed4InvalidParam = 6,
Failed4NoMemory = 7,
Failed4UnknownUser = 8,
Failed4MaxUser = 9,
Failed4FaceEnrolled = 10,
Failed4LivenessCheck = 12,
Failed4Timeout = 13,
Failed4Authorization = 14,
Failed4ReadFile = 19,
Failed4WriteFile = 20,
Failed4NoEncrypt = 21,
};
#pragma pack(1)
struct VerifyInfo {
uint8_t powerDownRightAway; // power down right away after verifying
uint8_t timeout; // timeout, unit second, default 10s
};
struct VerifyNoteInfo {
int16_t state; // corresponding to PALM_STATE_*
// position
int16_t left; // in pixel
int16_t top;
int16_t right;
int16_t bottom;
// pose
int16_t yaw; // up and down in vertical orientation
int16_t pitch; // right or left turned in horizontal orientation
int16_t roll; // slope
};
struct EnrollData {
uint8_t admin = 0;
uint8_t username[32];
uint8_t palmDirection;
uint8_t timeout;
};
struct EnrollDataReply {
uint16_t userid;
uint8_t face_direction; // depleted, user ignore this field
};
struct VerifyDataReply {
uint16_t userid;
uint8_t username[UsernameSize]; // 32Bytes uint8_t admin;
uint8_t unlockStatus;
};
#pragma pack()
explicit ModuleCommunication(QObject *parent = nullptr);
bool open(const QString &portName);
void verify(uint8_t timeout);
void enroll(const std::string &username, uint8_t timeout);
void deleteUser(uint16_t userid);
void deleteAll();
protected:
void onReadyRead();
std::pair<uint8_t *, uint32_t> generateFrame(MessageId command, const uint8_t *data = nullptr, uint16_t size = 0);
std::string protocolDataFormatString(const uint8_t *data, int size);
private:
std::shared_ptr<QSerialPort> m_serialPort;
};
#endif // MODULECOMMUNICATION_H

221
Analyser/Widget.cpp Normal file
View File

@ -0,0 +1,221 @@
#include "Widget.h"
#include "BoostLog.h"
#include "CategoryLogSinkBackend.h"
#include "ModuleCommunication.h"
#include <QComboBox>
#include <QFormLayout>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QSerialPortInfo>
#include <QTabWidget>
#include <QTextBrowser>
#include <QTimer>
#include <QVBoxLayout>
Widget::Widget(QWidget *parent) : QWidget{parent} {
auto serialGroupBox = new QGroupBox("串口设置");
auto serialLayout = new QGridLayout();
auto label = new QLabel("端口");
serialLayout->addWidget(label, 0, 0);
m_serialComboBox = new QComboBox();
serialLayout->addWidget(m_serialComboBox, 0, 1);
label = new QLabel("波特率");
serialLayout->addWidget(label, 1, 0);
label = new QLabel("115200");
serialLayout->addWidget(label, 1, 1);
auto refreshButton = new QPushButton("刷新");
connect(refreshButton, &QPushButton::clicked, this, &Widget::onSerialRefreshButtonClicked);
m_serialConnectButton = new QPushButton("连接");
connect(m_serialConnectButton, &QPushButton::clicked, this, &Widget::onSerialConnectButtonClicked);
serialLayout->addWidget(refreshButton, 2, 0);
serialLayout->addWidget(m_serialConnectButton, 2, 1);
serialGroupBox->setLayout(serialLayout);
auto uvcGroupBox = new QGroupBox("UVC设置");
auto uvcLayout = new QGridLayout();
label = new QLabel("设备名");
uvcLayout->addWidget(label, 0, 0);
label = new QLabel("未发现设备");
uvcLayout->addWidget(label, 0, 1);
auto uvcRefreshButton = new QPushButton("刷新");
auto uvcConnectButton = new QPushButton("连接");
uvcLayout->addWidget(uvcRefreshButton, 1, 0);
uvcLayout->addWidget(uvcConnectButton, 1, 1);
uvcGroupBox->setLayout(uvcLayout);
auto connectLayout = new QHBoxLayout();
connectLayout->addWidget(serialGroupBox);
connectLayout->addWidget(uvcGroupBox);
m_logBrowser = new QTextBrowser();
m_logBrowser->setReadOnly(true);
auto tabWidget = new QTabWidget();
tabWidget->addTab(m_logBrowser, "日志");
tabWidget->addTab(new QWidget(), "视频流");
tabWidget->addTab(new QWidget(), "已注册列表");
m_commandGroupBox = initializeCommandGroupBox();
auto operatorLayout = new QVBoxLayout();
operatorLayout->addLayout(connectLayout);
operatorLayout->addWidget(m_commandGroupBox);
operatorLayout->addStretch(2);
auto layout = new QHBoxLayout(this);
layout->addLayout(operatorLayout, 1);
layout->addWidget(tabWidget, 3);
QTimer::singleShot(0, this, [this]() {
onSerialRefreshButtonClicked();
m_commandGroupBox->setEnabled(false);
});
}
void Widget::initializeLogger() {
auto backend = boost::make_shared<CategoryLogSinkBackend>("GUI", m_logBrowser);
using SynchronousCategorySink = boost::log::sinks::synchronous_sink<CategoryLogSinkBackend>;
auto sink = boost::make_shared<SynchronousCategorySink>(backend);
// sink->set_formatter(&zlogFormatter);
boost::log::core::get()->add_sink(sink);
}
QGroupBox *Widget::initializeCommandGroupBox() {
auto ret = new QGroupBox("命令");
auto layout = new QGridLayout();
layout->addWidget(initializeEnrollGroupBox(), 0, 0);
layout->addWidget(initializeVerifyGroupBox(), 0, 1);
layout->addWidget(initializeDeleteGroupBox(), 1, 0);
layout->addWidget(initializePalmFeatureGroupBox(), 1, 1);
ret->setLayout(layout);
return ret;
}
QGroupBox *Widget::initializeEnrollGroupBox() {
auto ret = new QGroupBox("注册用户");
auto layout = new QFormLayout();
m_enrollNameEdit = new QLineEdit();
layout->addRow("用户姓名:", m_enrollNameEdit);
m_enrollTimeoutEdit = new QLineEdit("10");
layout->addRow("超时时间:", m_enrollTimeoutEdit);
m_enrollButton = new QPushButton("注册");
connect(m_enrollButton, &QPushButton::clicked, this, &Widget::onEnrollButtonClicked);
layout->addRow("", m_enrollButton);
ret->setLayout(layout);
return ret;
}
QGroupBox *Widget::initializeVerifyGroupBox() {
auto ret = new QGroupBox("识别用户");
auto layout = new QFormLayout();
m_verifyTimeoutEdit = new QLineEdit("10");
layout->addRow("超时时间:", m_verifyTimeoutEdit);
m_verifyButton = new QPushButton("识别");
connect(m_verifyButton, &QPushButton::clicked, this, &Widget::onVerifyButtonClicked);
layout->addRow("", m_verifyButton);
ret->setLayout(layout);
return ret;
}
QGroupBox *Widget::initializeDeleteGroupBox() {
auto ret = new QGroupBox("删除用户");
auto layout = new QFormLayout();
m_deleteIdEdit = new QLineEdit("");
layout->addRow("用户ID:", m_deleteIdEdit);
m_deleteButton = new QPushButton("删除");
connect(m_deleteButton, &QPushButton::clicked, this, &Widget::onDeleteButtonClicked);
layout->addRow("", m_deleteButton);
m_deleteAllButton = new QPushButton("删除所有");
connect(m_deleteAllButton, &QPushButton::clicked, this, &Widget::onDeleteAllButtonClicked);
layout->addRow("", m_deleteAllButton);
ret->setLayout(layout);
return ret;
}
QGroupBox *Widget::initializePalmFeatureGroupBox() {
auto ret = new QGroupBox("特征值下发/上报");
auto layout = new QFormLayout();
auto edit = new QLineEdit("");
layout->addRow("用户ID:", edit);
auto button = new QPushButton("特征值下发");
layout->addRow("", button);
auto button1 = new QPushButton("特征值上报");
layout->addRow("", button1);
ret->setLayout(layout);
return ret;
}
void Widget::onSerialConnectButtonClicked() {
auto button = dynamic_cast<QPushButton *>(sender());
if (button == nullptr) return;
auto text = button->text();
if (text == "连接") {
auto portName = m_serialComboBox->currentText();
m_communication = std::make_shared<ModuleCommunication>();
bool status = m_communication->open(portName);
if (status) {
m_commandGroupBox->setEnabled(true);
button->setText("关闭");
}
} else if (text == "关闭") {
m_communication.reset();
m_commandGroupBox->setEnabled(false);
button->setText("连接");
}
}
void Widget::onSerialRefreshButtonClicked() {
m_serialComboBox->clear();
auto ports = QSerialPortInfo::availablePorts();
for (auto &port : ports) {
m_serialComboBox->addItem(port.portName());
}
}
void Widget::onEnrollButtonClicked() {
auto name = m_enrollNameEdit->text();
auto timeout = m_enrollTimeoutEdit->text().toInt();
m_communication->enroll(name.toStdString(), timeout);
}
void Widget::onVerifyButtonClicked() {
if (!m_communication) return;
auto timeout = m_verifyTimeoutEdit->text().toInt();
m_communication->verify(timeout);
}
void Widget::onDeleteAllButtonClicked() {
if (!m_communication) return;
m_communication->deleteAll();
}
void Widget::onDeleteButtonClicked() {
if (!m_communication) return;
auto id = m_deleteIdEdit->text().toInt();
m_communication->deleteUser(id);
}

54
Analyser/Widget.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef WIDGET_H
#define WIDGET_H
#include "qgroupbox.h"
#include <QWidget>
class QPushButton;
class QTextBrowser;
class QComboBox;
class QLineEdit;
class ModuleCommunication;
class Widget : public QWidget {
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
void initializeLogger();
protected:
QGroupBox *initializeCommandGroupBox();
void onSerialConnectButtonClicked();
void onSerialRefreshButtonClicked();
void onEnrollButtonClicked();
void onVerifyButtonClicked();
void onDeleteAllButtonClicked();
void onDeleteButtonClicked();
QGroupBox *initializeEnrollGroupBox();
QGroupBox *initializeVerifyGroupBox();
QGroupBox *initializeDeleteGroupBox();
QGroupBox *initializePalmFeatureGroupBox();
private:
QComboBox *m_serialComboBox = nullptr;
QPushButton *m_serialConnectButton = nullptr;
QTextBrowser *m_logBrowser = nullptr;
QGroupBox *m_commandGroupBox = nullptr;
QLineEdit *m_enrollNameEdit = nullptr;
QLineEdit *m_enrollTimeoutEdit = nullptr;
QPushButton *m_enrollButton = nullptr;
QLineEdit *m_verifyTimeoutEdit = nullptr;
QPushButton *m_verifyButton = nullptr;
QLineEdit *m_deleteIdEdit = nullptr;
QPushButton *m_deleteButton = nullptr;
QPushButton *m_deleteAllButton = nullptr;
std::shared_ptr<ModuleCommunication> m_communication;
};
#endif // WIDGET_H

21
Analyser/main.cpp Normal file
View File

@ -0,0 +1,21 @@
#include "BoostLog.h"
#include "Widget.h"
#include <QApplication>
#include <QFont>
int main(int argc, char *argv[]) {
boost::log::initialize("logs/app");
QApplication a(argc, argv);
QFont font;
font.setPointSize(16);
a.setFont(font);
Widget w;
w.initializeLogger();
w.setWindowTitle("L015上位机工具");
w.setMinimumWidth(1120);
w.setMinimumHeight(640);
w.show();
return a.exec();
}