379 lines
11 KiB
C++
379 lines
11 KiB
C++
|
#include "LandslideAssess.h"
|
|||
|
|
|||
|
#include <QSettings>
|
|||
|
|
|||
|
LandslideAssess::LandslideAssess()
|
|||
|
{
|
|||
|
//ui.setupUi(this);
|
|||
|
}
|
|||
|
|
|||
|
QString LandslideAssess::PannelName()
|
|||
|
{
|
|||
|
return QString::fromLocal8Bit("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD>");
|
|||
|
}
|
|||
|
|
|||
|
QString LandslideAssess::CategoryName()
|
|||
|
{
|
|||
|
return QString::fromLocal8Bit("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD>");
|
|||
|
}
|
|||
|
|
|||
|
QString LandslideAssess::EnglishName()
|
|||
|
{
|
|||
|
return QString::fromLocal8Bit("LandslideAssess");
|
|||
|
}
|
|||
|
|
|||
|
QString LandslideAssess::ChineseName()
|
|||
|
{
|
|||
|
return QString::fromLocal8Bit("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>");
|
|||
|
}
|
|||
|
|
|||
|
QString LandslideAssess::Information()
|
|||
|
{
|
|||
|
return QString::fromLocal8Bit("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>");
|
|||
|
}
|
|||
|
|
|||
|
QString LandslideAssess::IconPath()
|
|||
|
{
|
|||
|
return QString(":/LandslideAssess/resources/assessment.svg");
|
|||
|
}
|
|||
|
|
|||
|
QWidget* LandslideAssess::CenterWidget()
|
|||
|
{
|
|||
|
QString gdal_path = qApp->applicationDirPath().toLocal8Bit() + "/share/gdal";
|
|||
|
QString pro_lib_path = qApp->applicationDirPath().toLocal8Bit() + "/share/proj";
|
|||
|
qputenv("GDAL_DATA", gdal_path.toLocal8Bit());
|
|||
|
qputenv("PROJ_LIB", pro_lib_path.toLocal8Bit());
|
|||
|
|
|||
|
bool showWin = false;
|
|||
|
if (myWidget == nullptr)
|
|||
|
{
|
|||
|
myWidget = new QDialog();
|
|||
|
showWin = true;
|
|||
|
qDebug() << "new QDialog()";
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
qDebug() << "already have myWidget";
|
|||
|
//<2F><><EFBFBD><EFBFBD>ڲ<EFBFBD><DAB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
myWidget->activateWindow();
|
|||
|
myWidget->raise();
|
|||
|
return myWidget;
|
|||
|
}
|
|||
|
ui.setupUi(myWidget);
|
|||
|
|
|||
|
myWidget->setWindowTitle(QString::fromLocal8Bit("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"));
|
|||
|
myWidget->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint);
|
|||
|
myWidget->setWindowIcon(QIcon(":/LandslideAssess/resources/assessment.svg"));
|
|||
|
myWidget->setAttribute(Qt::WA_QuitOnClose, false);
|
|||
|
|
|||
|
myWidget->setAttribute(Qt::WA_DeleteOnClose);
|
|||
|
connect(myWidget, &QDialog::destroyed, this, [=] {
|
|||
|
qDebug() << "----Landslide assess window close----";
|
|||
|
|
|||
|
QDir pluginsDir = QDir(qApp->applicationDirPath());
|
|||
|
if (pluginsDir.cd("srsplugins\\SldModel"))
|
|||
|
{
|
|||
|
QString strConfigPath = pluginsDir.absoluteFilePath("sld_config.ini");
|
|||
|
QFile f(strConfigPath);
|
|||
|
if (f.exists())
|
|||
|
{
|
|||
|
WriteConfigPaths(strConfigPath);
|
|||
|
f.close();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (mWorkThread != nullptr)
|
|||
|
{
|
|||
|
mWorkThread->requestInterruption();//<2F><><EFBFBD><EFBFBD><EFBFBD>߳<EFBFBD><DFB3>ж<EFBFBD>
|
|||
|
mWorkThread->quit();
|
|||
|
mWorkThread->wait();//<2F><><EFBFBD><EFBFBD>wait<69><74><EFBFBD>ȵ<EFBFBD><C8B5><EFBFBD>finished<65>źŶ<C5BA>Ӧ<EFBFBD>IJۺ<C4B2><DBBA><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD><D6B4><EFBFBD><EFBFBD><EFBFBD>ɺ<EFBFBD><C9BA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
mWorker->on_cancel();
|
|||
|
mWorkThread = nullptr;//mWorkThread, &QThread::finished, mWorkThread, &QObject::deleteLater<65><72><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫdelete
|
|||
|
mWorker = nullptr;//mWorkThread, &QThread::finished, mWorker, &QObject::deleteLater
|
|||
|
}
|
|||
|
//qDebug() << "--destroyed";
|
|||
|
myWidget->close();
|
|||
|
myWidget = nullptr;
|
|||
|
});
|
|||
|
connect(ui.pbtInDataset, &QPushButton::clicked, this, &LandslideAssess::choosePredictPath);
|
|||
|
connect(ui.pbtInLabel, &QPushButton::clicked, this, &LandslideAssess::chooseLabelPath);
|
|||
|
connect(ui.pbtIndex, &QPushButton::clicked, this, &LandslideAssess::chooseAssessFile);
|
|||
|
connect(ui.pbtInModel, &QPushButton::clicked, this, &LandslideAssess::chooseModelFile);
|
|||
|
connect(ui.pbtResult, &QPushButton::clicked, this, &LandslideAssess::chooseResultPath);
|
|||
|
|
|||
|
connect(ui.pushButton_ok, &QPushButton::clicked, this, &LandslideAssess::readAndStart);
|
|||
|
connect(ui.pushButton_cancel, &QPushButton::clicked, this, &LandslideAssess::pbCancel);
|
|||
|
|
|||
|
ui.progressBar->setTextVisible(true);
|
|||
|
ui.progressBar->setRange(0, 100);
|
|||
|
|
|||
|
ui.pbtInModel->setFocus();
|
|||
|
|
|||
|
QFile qssFile(":/LandslideAssess/LandslideAssess.qss");
|
|||
|
qssFile.open(QFile::ReadOnly); //<2F><>ֻ<EFBFBD><D6BB><EFBFBD><EFBFBD>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD>
|
|||
|
if (qssFile.isOpen())
|
|||
|
{
|
|||
|
QString qss = QLatin1String(qssFile.readAll());
|
|||
|
myWidget->setStyleSheet(qss);
|
|||
|
qssFile.close();
|
|||
|
}
|
|||
|
else
|
|||
|
qDebug() << "no qssFile";
|
|||
|
|
|||
|
QDir pluginsDir = QDir(qApp->applicationDirPath());
|
|||
|
if (pluginsDir.cd("srsplugins\\SldModel"))
|
|||
|
{
|
|||
|
QString strConfigPath = pluginsDir.absoluteFilePath("sld_config.ini");
|
|||
|
QFile f(strConfigPath);
|
|||
|
if (f.exists())
|
|||
|
{
|
|||
|
ReadConfigHistoryPaths(strConfigPath);
|
|||
|
f.close();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (showWin)
|
|||
|
myWidget->show();
|
|||
|
|
|||
|
return myWidget;
|
|||
|
}
|
|||
|
|
|||
|
void LandslideAssess::startWorkThread()
|
|||
|
{
|
|||
|
if (mWorker != nullptr)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
mWorkThread = new QThread();
|
|||
|
mWorker = new WorkObject();
|
|||
|
mWorker->moveToThread(mWorkThread);
|
|||
|
connect(mWorkThread, &QThread::finished, mWorkThread, &QObject::deleteLater);
|
|||
|
connect(mWorkThread, &QThread::finished, mWorker, &QObject::deleteLater);
|
|||
|
connect(mWorker, &WorkObject::progress, myWidget, [=](int value) {
|
|||
|
ui.progressBar->setValue(value);
|
|||
|
if (value == 100)
|
|||
|
{
|
|||
|
QString txtDir = ui.lineResult->text() + "/assessment.txt";
|
|||
|
QFile file(txtDir);
|
|||
|
QString outLines = "";
|
|||
|
if (file.open(QIODevice::ReadOnly))
|
|||
|
{
|
|||
|
QTextStream stream(&file);
|
|||
|
// recall <20>ٻ<EFBFBD><D9BB><EFBFBD>
|
|||
|
// precision <20><>ȷ<EFBFBD><C8B7>
|
|||
|
// mIoU ƽ<><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
// mAP ƽ<><C6BD><EFBFBD><EFBFBD><EFBFBD>Ⱦ<EFBFBD>ֵ
|
|||
|
// f1_score
|
|||
|
while (!stream.atEnd())
|
|||
|
{
|
|||
|
QString line = stream.readLine();
|
|||
|
//strs.push_back(line);
|
|||
|
if (line.contains("===>mIoU"))
|
|||
|
{
|
|||
|
//===>mIoU:68.45; mAP: 74.92
|
|||
|
line.remove("===>");
|
|||
|
line.replace(QStringLiteral("mAP"), QStringLiteral("ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ⱦ<EFBFBD>ֵmAP"));
|
|||
|
line.replace(QStringLiteral("mIoU"), QStringLiteral("ƽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>mIoU"));
|
|||
|
outLines = outLines + line + "\n";
|
|||
|
}
|
|||
|
else if (line.contains("===>recall"))
|
|||
|
{
|
|||
|
//===>recall:66.62; precision: 51.2
|
|||
|
line.remove("===>");
|
|||
|
line.replace(QStringLiteral("recall"), QStringLiteral("<EFBFBD>ٻ<EFBFBD><EFBFBD><EFBFBD>recall"));
|
|||
|
line.replace(QStringLiteral("precision"), QStringLiteral("<EFBFBD><EFBFBD>ȷ<EFBFBD><EFBFBD>precision"));
|
|||
|
outLines = outLines + line + "\n";
|
|||
|
}
|
|||
|
else if (line.contains("===>f1_score"))
|
|||
|
{
|
|||
|
//===>f1_score:57.9
|
|||
|
line.remove("===>");
|
|||
|
line.replace(QStringLiteral("f1_score"), QStringLiteral("F1<EFBFBD><EFBFBD><EFBFBD><EFBFBD>f1_score"));
|
|||
|
outLines = outLines + line;
|
|||
|
}
|
|||
|
}
|
|||
|
file.close();
|
|||
|
}
|
|||
|
|
|||
|
QMessageBox mess(QMessageBox::Information,
|
|||
|
QString::fromLocal8Bit("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
|
|||
|
QString::fromLocal8Bit("<EFBFBD><EFBFBD><EFBFBD>۽<EFBFBD><EFBFBD><EFBFBD>:\n") + txtDir + "\n"
|
|||
|
+ outLines);
|
|||
|
mess.setWindowFlags(Qt::Drawer);
|
|||
|
mess.setStandardButtons(QMessageBox::Yes);
|
|||
|
mess.button(QMessageBox::StandardButton::Yes)->setText(QString::fromLocal8Bit("ȷ<EFBFBD><EFBFBD>"));
|
|||
|
int result = mess.exec();
|
|||
|
pbCancel();
|
|||
|
}
|
|||
|
});
|
|||
|
connect(this, &LandslideAssess::startPreAssessment, mWorker, &WorkObject::runAssessWork);
|
|||
|
|
|||
|
mWorkThread->start();
|
|||
|
}
|
|||
|
|
|||
|
void LandslideAssess::ReadConfigHistoryPaths(QString strPath)
|
|||
|
{
|
|||
|
QSettings configIni(strPath, QSettings::IniFormat);
|
|||
|
|
|||
|
//<2F><EFBFBD><F2BFAAB1><EFBFBD>Ϊ<EFBFBD><CEAA>[DemTrain] <20><><EFBFBD>飬<EFBFBD><E9A3AC>ȡDemTrain<69><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>TrainResult<6C>ֶ<EFBFBD>
|
|||
|
configIni.beginGroup("SldTrain");
|
|||
|
|
|||
|
QString strDemTrainResult = configIni.value("TrainResult").toString();
|
|||
|
QDir srcDir = QDir(strDemTrainResult);
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC>б<EFBFBD>
|
|||
|
if (srcDir.absoluteFilePath("val_list.txt") != "")
|
|||
|
ui.lineIndex->setText(srcDir.absoluteFilePath("val_list.txt"));
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Images
|
|||
|
srcDir = QDir(strDemTrainResult);
|
|||
|
if (srcDir.cd("Images"))
|
|||
|
ui.lineInDataset->setText(srcDir.absolutePath());
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Labels
|
|||
|
srcDir = QDir(strDemTrainResult);
|
|||
|
if (srcDir.cd("Labels"))
|
|||
|
ui.lineInLabel->setText(srcDir.absolutePath());
|
|||
|
|
|||
|
configIni.endGroup();//<2F>ر<EFBFBD><D8B1><EFBFBD>
|
|||
|
|
|||
|
//<2F><EFBFBD><F2BFAAB1><EFBFBD>Ϊ<EFBFBD><CEAA>[DemAssess] <20><><EFBFBD>飬<EFBFBD><E9A3AC>ȡAssessResult<6C>ֶ<EFBFBD>
|
|||
|
configIni.beginGroup("SldAssess");
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>۽<EFBFBD><DBBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
ui.lineResult->setText(configIni.value("AssessResult").toString());
|
|||
|
|
|||
|
configIni.endGroup();//<2F>ر<EFBFBD><D8B1><EFBFBD>
|
|||
|
}
|
|||
|
|
|||
|
void LandslideAssess::WriteConfigPaths(QString strPath)
|
|||
|
{
|
|||
|
QSettings configIni(strPath, QSettings::IniFormat);
|
|||
|
configIni.setIniCodec("utf-8");
|
|||
|
//<2F><EFBFBD><F2BFAAB1><EFBFBD>Ϊ<EFBFBD><CEAA>[DemAssess] <20><><EFBFBD><EFBFBD>
|
|||
|
configIni.beginGroup("SldAssess");
|
|||
|
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><C4A3>·<EFBFBD><C2B7>
|
|||
|
QString temp = ui.lineResult->text();
|
|||
|
if (temp != "")
|
|||
|
configIni.setValue("AssessResult", temp);
|
|||
|
|
|||
|
configIni.endGroup();//<2F>ر<EFBFBD><D8B1><EFBFBD>
|
|||
|
}
|
|||
|
|
|||
|
void LandslideAssess::readAndStart()
|
|||
|
{
|
|||
|
QString dirModel = ui.lineInModel->text();
|
|||
|
QString dirIndex = ui.lineIndex->text();
|
|||
|
QString dirLabel = ui.lineInLabel->text();
|
|||
|
QString dirPredict = ui.lineInDataset->text();
|
|||
|
QString dirResult = ui.lineResult->text();
|
|||
|
|
|||
|
ui.progressBar->setValue(0);
|
|||
|
|
|||
|
if (dirLabel == "" || dirPredict == "" || dirIndex == "" || dirModel == "" || dirResult == "")
|
|||
|
{
|
|||
|
QMessageBox mess(QMessageBox::NoIcon, QString::fromLocal8Bit("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>"), QString::fromLocal8Bit("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>"));
|
|||
|
mess.setWindowFlags(Qt::Drawer);
|
|||
|
int result = mess.exec();
|
|||
|
return;
|
|||
|
}
|
|||
|
if (mWorkThread == nullptr)
|
|||
|
{
|
|||
|
qDebug() << "--startThread";
|
|||
|
startWorkThread();
|
|||
|
}
|
|||
|
emit startPreAssessment(dirModel, dirPredict, dirLabel, dirIndex, dirResult);
|
|||
|
}
|
|||
|
|
|||
|
void LandslideAssess::pbCancel()
|
|||
|
{
|
|||
|
qDebug() << "--pbtCancel";
|
|||
|
delete myWidget;//<2F><><EFBFBD><EFBFBD>&QDialog::destroyed
|
|||
|
}
|
|||
|
|
|||
|
void LandslideAssess::choosePredictPath()
|
|||
|
{
|
|||
|
QString dirInDataset = QFileDialog::getExistingDirectory(ui.pbtInDataset, QString::fromLocal8Bit("ѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>·<EFBFBD><EFBFBD>"), "");
|
|||
|
if (dirInDataset != "")
|
|||
|
ui.lineInDataset->setText(dirInDataset);
|
|||
|
}
|
|||
|
|
|||
|
void LandslideAssess::chooseLabelPath()
|
|||
|
{
|
|||
|
QString dirDom = QFileDialog::getExistingDirectory(ui.pbtInLabel, QString::fromLocal8Bit("ѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǩ<EFBFBD>ļ<EFBFBD>·<EFBFBD><EFBFBD>"), "");
|
|||
|
if (dirDom != "")
|
|||
|
ui.lineInLabel->setText(dirDom);
|
|||
|
}
|
|||
|
|
|||
|
void LandslideAssess::chooseAssessFile()
|
|||
|
{
|
|||
|
QString dirDom = QFileDialog::getOpenFileName(ui.pbtIndex, QString::fromLocal8Bit("ѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"), "", "*.txt");
|
|||
|
if (dirDom != "")
|
|||
|
ui.lineIndex->setText(dirDom);
|
|||
|
}
|
|||
|
|
|||
|
void LandslideAssess::chooseModelFile()
|
|||
|
{
|
|||
|
QString fileModel = QFileDialog::getOpenFileName(ui.pbtInModel, QString::fromLocal8Bit("ѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ģ<EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>"), "", "*.pth");
|
|||
|
if (fileModel != "")
|
|||
|
ui.lineInModel->setText(fileModel);
|
|||
|
}
|
|||
|
|
|||
|
void LandslideAssess::chooseResultPath()
|
|||
|
{
|
|||
|
QString dirResult = QFileDialog::getExistingDirectory(ui.pbtResult, QString::fromLocal8Bit("ѡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><EFBFBD>"), "");
|
|||
|
if (dirResult != "")
|
|||
|
ui.lineResult->setText(dirResult);
|
|||
|
}
|
|||
|
|
|||
|
void WorkObject::runAssessWork(QString model, QString PRE, QString label, QString index, QString result)
|
|||
|
{
|
|||
|
qDebug() << "start work:" << "\npredict: " << PRE << "\nLabel:" << label;
|
|||
|
QFileInfo file(index);
|
|||
|
file.absolutePath();
|
|||
|
assess_txt_path = file.absolutePath() + "/assessment.txt";
|
|||
|
QDir pluginsDir = QDir(qApp->applicationDirPath());
|
|||
|
qDebug() << "----" << pluginsDir.currentPath();
|
|||
|
if (!pluginsDir.cd("models\\envs"))
|
|||
|
{
|
|||
|
qDebug() << "no folder models\\envs";
|
|||
|
return;
|
|||
|
}
|
|||
|
QString exeDirName = pluginsDir.absoluteFilePath("miou_landslide.exe");
|
|||
|
QString model_dir = " --model_dir " + model;
|
|||
|
QString img_data_dir = " --img_data_dir " + PRE + "/";
|
|||
|
QString label_dir = " --label_dir " + label + "/";
|
|||
|
QString val_list = " --val_list " + index;
|
|||
|
QString out_result = " --result_file " + result + "/";
|
|||
|
QString ss = exeDirName + model_dir + img_data_dir + label_dir + val_list + out_result;
|
|||
|
qDebug() << ss;
|
|||
|
QProcess* pProces = new QProcess(this);
|
|||
|
connect(pProces, SIGNAL(readyReadStandardOutput()), this, SLOT(writeAssessTxt()));
|
|||
|
pProces->start(ss);
|
|||
|
|
|||
|
emit progress(2);
|
|||
|
}
|
|||
|
|
|||
|
void WorkObject::writeAssessTxt()
|
|||
|
{
|
|||
|
mProcess = (QProcess*)sender();
|
|||
|
QString output = QString::fromLocal8Bit(mProcess->readAllStandardOutput());
|
|||
|
output.chop(2);
|
|||
|
if (output.toFloat() != 0)
|
|||
|
{
|
|||
|
qDebug() << "process:" << output.toFloat();
|
|||
|
emit progress(output.toFloat());
|
|||
|
}
|
|||
|
else
|
|||
|
qDebug() << "unsolved exe out:" << output;
|
|||
|
}
|
|||
|
|
|||
|
void WorkObject::on_cancel()
|
|||
|
{
|
|||
|
if (mProcess == nullptr)
|
|||
|
qDebug() << "--mProces null";
|
|||
|
else
|
|||
|
{
|
|||
|
QString KillStr = "taskkill /f /im miou_landslide.exe";
|
|||
|
mProcess->startDetached(KillStr);
|
|||
|
qDebug() << "--kill Proces";
|
|||
|
}
|
|||
|
}
|