#include "ctrladvancedpanel.h"
#include "globaldefine.h"
#include "base/waitingdlg.h"
#include "base/changepasswordform.h"
#include "tools.h"
#include "gutil/qgui.h"
#include "gutil/qnetwork.h"
#include "gutil/qjson.h"
#include "program/ephoto.h"
#include "upgradeapkdialog.h"
#include "deviceitem.h"
#include <QComboBox>
#include <QFileDialog>
#include <QJsonArray>
#include <QJsonObject>
#include <QKeyEvent>
#include <QMap>
#include <QMessageBox>
#include <QMetaEnum>
#include <QProcess>
#include <QSettings>
#include <QUuid>
#include <QHeaderView>
#include <QColorDialog>
#include <QSpinBox>
#include <QButtonGroup>
#include "devicepanel.h"
#include <QDialogButtonBox>

CtrlAdvancedPanel::CtrlAdvancedPanel() {
    setFocusPolicy(Qt::StrongFocus);

    auto vBox = new QVBoxLayout(this);

    lbTitle = new QLabel;
    lbTitle->setAlignment(Qt::AlignCenter);
    vBox->addWidget(lbTitle);

    auto hBox = new HBox(vBox);

    lbScreenWidth = new QLabel;
    hBox->addWidget(lbScreenWidth);

    fdScreenWidth = new QLineEdit;
    fdScreenWidth->setMaximumWidth(60);
    hBox->addWidget(fdScreenWidth);

    lbScreenHeight = new QLabel;
    hBox->addWidget(lbScreenHeight);

    fdScreenHeight = new QLineEdit;
    fdScreenHeight->setMaximumWidth(60);
    hBox->addWidget(fdScreenHeight);

    btnScreenSet = new QPushButton;
    btnScreenSet->setProperty("ssType", "progManageTool");
    connect(btnScreenSet, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        auto width = fdScreenWidth->text();
        if(width.isEmpty()) {
            QMessageBox::information(this, tr("Tip"),tr("InputWidthTip"));
            fdScreenWidth->setFocus();
            return;
        }
        auto height = fdScreenHeight->text();
        if(height.isEmpty()) {
            QMessageBox::information(this, tr("Tip"),tr("InputHeightTip"));
            fdScreenHeight->setFocus();
            return;
        }
        QJsonObject json;
        json.insert("_id", "SetScreenSize");
        json.insert("_type", "SetScreenSize");
        json.insert("width", width.toInt());
        json.insert("height", height.toInt());
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("SetScreenSize"));
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("SetScreenSize"))
            }
        }
    });
    hBox->addWidget(btnScreenSet);

    hBox->addSpacing(20);

    lbAlias = new QLabel;
    hBox->addWidget(lbAlias);

    fdAlias = new QLineEdit;
    fdAlias->setMaximumWidth(200);
    hBox->addWidget(fdAlias);

    btnAliasSet = new QPushButton;
    btnAliasSet->setProperty("ssType", "progManageTool");
    connect(btnAliasSet, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        auto alias = fdAlias->text();
        QJsonObject json;
        json.insert("_id", "SetCardAlias");
        json.insert("_type", "SetCardAlias");
        json.insert("alias", QString::fromLatin1(alias.toUtf8().toBase64()));
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("SetCardAlias"));
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
                auto item = findItem(card.id);
                if(item) {
                    item->mCard.alias = alias;
                    item->setText(DeviceTable_Remark, alias);
                }
            });
        } else {
            foreach(auto card, gSelCards) {
                auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                connect(reply, &QNetworkReply::finished, gFdResInfo, [=] {
                    QString err = checkReplyForJson(reply);
                    gFdResInfo->append(card.id+" "+tr("SetCardAlias")+" "+(err.isEmpty() ? QCoreApplication::translate("Def","Success") : err));
                    if(! err.isEmpty()) return;
                    auto item = findItem(card.id);
                    if(item) {
                        item->mCard.alias = alias;
                        item->setText(DeviceTable_Remark, alias);
                    }
                });
            }
        }
    });
    hBox->addWidget(btnAliasSet);

    hBox->addStretch();

    hBox = new HBox(vBox);

    labelWebServer = new QLabel;
    hBox->addWidget(labelWebServer);

    fdWebServerAddr = new QComboBox;
    fdWebServerAddr->addItem("www.m2mled.net");
    fdWebServerAddr->addItem("www.ledaips.com");
    fdWebServerAddr->addItem("https://www.taxihub.cn:2340");
    fdWebServerAddr->addItem("https://www.ledaips.com:2340");
    fdWebServerAddr->addItem("https://www.36taxi.com:2340");
    fdWebServerAddr->addItem("www.tlzxled.com");
    fdWebServerAddr->setMinimumWidth(260);
    fdWebServerAddr->setEditable(true);
    hBox->addWidget(fdWebServerAddr);

    lbCompanyId = new QLabel;
    hBox->addWidget(lbCompanyId);

    fdCompanyId = new QLineEdit;
    fdCompanyId->setFixedWidth(100);
    hBox->addWidget(fdCompanyId);

    btnWebServerSet = new QPushButton;
    btnWebServerSet->setProperty("ssType", "progManageTool");
    connect(btnWebServerSet, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }

        QString serverAddr = fdWebServerAddr->currentText();
        if(serverAddr.isEmpty()) {
            QMessageBox::information(this, tr("Tip"),tr("InputWebServerAddressTip"));
            fdWebServerAddr->setFocus();
            return;
        }
        auto companyId = fdCompanyId->text();
        if(companyId.isEmpty()) {
            QMessageBox::information(this, tr("Tip"),tr("InputCompanyIdTip"));
            fdCompanyId->setFocus();
            return;
        }
        auto res = QMessageBox::question(this, tr("Tip Info"), tr("Do you want to modify webserveraddress and companyId?"));
        if(res != QMessageBox::Yes) return;
        QJsonObject json;
        json.insert("_id", "SetOnlineAddr");
        json.insert("_type", "SetOnlineAddr");
        json.insert("server", serverAddr);
        json.insert("companyID", companyId);
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("SetOnlineAddr"));
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("SetOnlineAddr"))
            }
        }
    });
    hBox->addWidget(btnWebServerSet);

    hBox->addStretch();

    hBox = new HBox(vBox);

    label = new QLabel;
    hBox->addWidget(label);

    fdRealtimeServer = new QComboBox;
    fdRealtimeServer->addItem(tr("www.ledokcloud.com/realtime"));
    fdRealtimeServer->setMinimumWidth(260);
    fdRealtimeServer->setEditable(true);
    hBox->addWidget(fdRealtimeServer);

    btnRealtimeServerSet = new QPushButton;
    btnRealtimeServerSet->setProperty("ssType", "progManageTool");
    connect(btnRealtimeServerSet, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "SetRealtimeServer");
        json.insert("_type", "SetRealtimeServer");
        json.insert("server", fdRealtimeServer->currentText());
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("SetRealtimeServer")+" ...");
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("SetRealtimeServer"))
            }
        }
    });
    hBox->addWidget(btnRealtimeServerSet);

    btnRealtimeClear = new QPushButton;
    btnRealtimeClear->setProperty("ssType", "progManageTool");
    connect(btnRealtimeClear, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        fdRealtimeServer->clearEditText();
        QJsonObject json;
        json.insert("_id", "SetRealtimeServer");
        json.insert("_type", "SetRealtimeServer");
        json.insert("server", "");
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("ClearRealtimeServer")+" ...");
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("ClearRealtimeServer"))
            }
        }
    });
    hBox->addWidget(btnRealtimeClear);

    hBox->addStretch();


    hBox = new HBox(vBox);

    btnWareUpdate = new QPushButton;
    btnWareUpdate->setMinimumSize(100, 30);
    btnWareUpdate->setProperty("ssType", "progManageTool");
    connect(btnWareUpdate, &QPushButton::clicked, this, [=] {
        UpgradeApkDialog dlg(this);
        dlg.exec();
    });
    hBox->addWidget(btnWareUpdate);

    lbWareTip = new QLabel;
    hBox->addWidget(lbWareTip);
    hBox->addStretch();

    hBox = new HBox(vBox);

    btnApkCheck = new QPushButton;
    btnApkCheck->setProperty("ssType", "progManageTool");
    connect(btnApkCheck, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "CheckSoftVersions");
        json.insert("_type", "CheckSoftVersions");
        if(gSelCards.count() != 1) return;
        auto waitingDlg = new WaitingDlg(this, tr("Check Apk Version"));
        Def_CtrlReqPre
        connect(reply, &QNetworkReply::finished, this, [this, reply, waitingDlg] {
            Def_CtrlSingleGetReply
            waitingDlg->close();
            fdPkg->clear();
            auto apps = json["apps"].toArray();
            auto infoDlg = new QDialog(this);
            infoDlg->setAttribute(Qt::WA_DeleteOnClose);
#ifdef Q_OS_WIN
            infoDlg->setWindowFlag(Qt::WindowContextHelpButtonHint, false);
#endif
            infoDlg->resize(500, 500);
            infoDlg->setWindowTitle(tr("Software Version Info"));
            auto vBox = new QVBoxLayout(infoDlg);
            vBox->setContentsMargins(0, 0, 0, 0);
            auto table = new Table{
                {"apk", "Apk"},
                {"ver", tr("Version")},
                {"pkg", tr("Package")}
            };
            table->setDefs();
            table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
            table->setRowCount(apps.size());
            vBox->addWidget(table);
            for(int i=0; i<apps.size(); i++) {
                auto app = apps.at(i);
                QString packageName = app["packageName"].toString();
                table->setItem(i, "apk", new QTableWidgetItem{app["appName"].toString()});
                table->setItem(i, "ver", new QTableWidgetItem{app["versionName"].toString()});
                table->setItem(i, "pkg", new QTableWidgetItem{packageName});
                if(! (packageName.endsWith(".cardsystem") || packageName.endsWith(".systemcore") || packageName.endsWith(".update"))) fdPkg->addItem(packageName);
            }
            infoDlg->show();
            infoDlg->raise();
            infoDlg->activateWindow();
        });
    });
    hBox->addWidget(btnApkCheck);

    fdPkg = new QComboBox;
    fdPkg->setEditable(true);
    fdPkg->setMinimumWidth(200);
    hBox->addWidget(fdPkg);

    fdUninstall = new QPushButton;
    fdUninstall->setProperty("ssType", "progManageTool");
    connect(fdUninstall, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        auto pkg = fdPkg->currentText();
        if(pkg.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("Package name is null"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "UninstallSoftware");
        json.insert("_type", "UninstallSoftware");
        json.insert("packageName", pkg);
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("UninstallSoftware"));
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("UninstallSoftware"))
            }
        }
    });
    hBox->addWidget(fdUninstall);

    btnIsRunning = new QPushButton;
    btnIsRunning->setProperty("ssType", "progManageTool");
    connect(btnIsRunning, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        auto pkg = fdPkg->currentText();
        if(pkg.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("Package name is null"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "IsSoftwareRunning");
        json.insert("_type", "IsSoftwareRunning");
        json.insert("packageName", pkg);
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("Check apk running status"));
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSingleGetReply
                waitingDlg->close();
                QMessageBox::information(this, tr("Tip"), json["running"].toBool() ? tr("running") : tr("no running"));
            });
        } else {
            foreach(auto card, gSelCards) {
                auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                auto cardId = card.id;
                connect(reply, &QNetworkReply::finished, this, [reply, cardId] {
                    QJsonDocument json;
                    QString err = checkReplyForJson(reply, &json);
                    gFdResInfo->append(cardId+" "+tr("Check apk running status")+" "+(err.isEmpty() ? (json["running"].toBool() ? tr("running") : tr("no running")) : err));
                });
            }
        }
    });
    hBox->addWidget(btnIsRunning);
    hBox->addStretch();

    hBox = new HBox(vBox);

    btnRestart = new QPushButton;
    btnRestart->setProperty("ssType", "progManageTool");
    connect(btnRestart, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "RestartAndroid");
        json.insert("_type", "RestartAndroid");
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("RestartAndroid")+" ...");
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("RestartAndroid"))
            }
        }
    });
    hBox->addWidget(btnRestart);

    btnClearProg = new QPushButton;
    btnClearProg->setProperty("ssType", "progManageTool");
    connect(btnClearProg, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        JObj json{{"_type","DelPrograms"},{"_id","DelPrograms"},{"zVer","xixun1"}};
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("Clearing Program")+" ...");
            waitingDlg->show();
            auto card = gSelCards[0];
            auto tcp = new TcpSocket;
            connect(waitingDlg, &WaitingDlg::rejected, tcp, [=] {
                tcp->abort();
                tcp->deleteLater();
            });
            connect(tcp, &QTcpSocket::connected, tcp, [=] {
                tcp->stopTimer();
                tcp->write(JToBytes(json));
                tcp->startTimer(10000);
            });
            connect(tcp, &QTcpSocket::readyRead, tcp, [=] {
                tcp->stopTimer();
                auto resp = tcp->readAll();
                tcp->close();
                tcp->deleteLater();
                QString error;
                auto json = JFrom(resp, &error);
                if(! error.isEmpty()) {
                    waitingDlg->close();
                    QMessageBox::critical(this, tr("Tip"), error);
                } else if(! json["success"].toBool()) {
                    waitingDlg->close();
                    QMessageBox::critical(this, tr("Tip"), tr("Clear Program")+" "+tr("Failed"));
                } else waitingDlg->success();
            });
            connect(tcp, &QTcpSocket::errorOccurred, tcp, [=](QAbstractSocket::SocketError err) {
                tcp->close();
                tcp->deleteLater();
                waitingDlg->close();
                QMessageBox::critical(this, tr("Tip"), QString(socketErrKey(err))+" ("+QString::number(err)+") "+tcp->errorString());
            });
            tcp->connectToHost(card.ip, 3333);
            tcp->startTimer(10000);
        } else {
            foreach(auto card, gSelCards) {
                auto tcp = new TcpSocket;
                auto cardId = card.id;
                connect(tcp, &QTcpSocket::connected, tcp, [=] {
                    tcp->stopTimer();
                    tcp->write(JToBytes(json));
                    tcp->startTimer(10000);
                });
                connect(tcp, &QTcpSocket::readyRead, tcp, [=] {
                    tcp->stopTimer();
                    auto resp = tcp->readAll();
                    tcp->close();
                    tcp->deleteLater();
                    QString error;
                    auto json = JFrom(resp, &error);
                    if(! error.isEmpty()) gFdResInfo->append(cardId+" "+tr("Clear Program")+" "+error);
                    else if(! json["success"].toBool()) gFdResInfo->append(cardId+" "+tr("Clear Program")+" "+tr("Failed"));
                    else gFdResInfo->append(cardId+" "+tr("Clear Program")+" "+tr("Success"));
                });
                connect(tcp, &QTcpSocket::errorOccurred, tcp, [=](QAbstractSocket::SocketError err) {
                    tcp->close();
                    tcp->deleteLater();
                    gFdResInfo->append(cardId+" "+tr("Clear Program")+" "+socketErrKey(err)+" ("+QString::number(err)+") "+tcp->errorString());
                });
                tcp->connectToHost(card.ip, 3333);
                tcp->startTimer(10000);
            }
        }
    });
    hBox->addWidget(btnClearProg);

    btnGetPlayerState = new QPushButton;
    btnGetPlayerState->setProperty("ssType", "progManageTool");
    connect(btnGetPlayerState, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        JObj json{{"_type","getPlayerState"},{"_id","getPlayerState"},{"zVer","xixun1"}};
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("Getting Player State")+" ...");
            waitingDlg->showLater();
            auto card = gSelCards[0];
            auto tcp = new TcpSocket;
            connect(waitingDlg, &WaitingDlg::rejected, tcp, [=] {
                tcp->abort();
                tcp->deleteLater();
            });
            connect(tcp, &QTcpSocket::connected, tcp, [=] {
                tcp->stopTimer();
                tcp->write(JToBytes(json));
                tcp->startTimer(10000);
            });
            connect(tcp, &QTcpSocket::readyRead, tcp, [=] {
                tcp->stopTimer();
                auto resp = tcp->readAll();
                tcp->close();
                tcp->deleteLater();
                waitingDlg->close();
                QString error;
                auto json = JFrom(resp, &error);
                if(! error.isEmpty()) {
                    QMessageBox::critical(this, tr("Error"), error);
                    return;
                }
                QMessageBox::information(this, tr("Player State"), json["code"].toStr()+". "+json["des_en"].toStr()+"\n"+json["des"].toStr());
            });
            connect(tcp, &QTcpSocket::errorOccurred, tcp, [=](QAbstractSocket::SocketError err) {
                tcp->close();
                tcp->deleteLater();
                waitingDlg->close();
                QMessageBox::critical(this, tr("Error"), QString(socketErrKey(err))+" ("+QString::number(err)+") "+tcp->errorString());
            });
            tcp->connectToHost(card.ip, 3333);
            tcp->startTimer(10000);
        } else {
            foreach(auto card, gSelCards) {
                auto tcp = new TcpSocket;
                auto cardId = card.id;
                connect(tcp, &QTcpSocket::connected, tcp, [=] {
                    tcp->stopTimer();
                    tcp->write(JToBytes(json));
                    tcp->startTimer(10000);
                });
                connect(tcp, &QTcpSocket::readyRead, tcp, [=] {
                    tcp->stopTimer();
                    auto resp = tcp->readAll();
                    tcp->close();
                    tcp->deleteLater();
                    QString error;
                    auto json = JFrom(resp, &error);
                    if(! error.isEmpty()) {
                        gFdResInfo->append(cardId+" "+tr("Get Player State")+" "+error);
                        return;
                    }
                    gFdResInfo->append(cardId+" "+tr("Player State")+" "+json["code"].toStr()+". "+json["des_en"].toStr()+"\n"+json["des"].toStr());
                });
                connect(tcp, &QTcpSocket::errorOccurred, tcp, [=](QAbstractSocket::SocketError err) {
                    tcp->close();
                    tcp->deleteLater();
                    gFdResInfo->append(cardId+" "+tr("Get Player State")+" "+socketErrKey(err)+" ("+QString::number(err)+") "+tcp->errorString());
                });
                tcp->connectToHost(card.ip, 3333);
                tcp->startTimer(10000);
            }
        }
    });
    hBox->addWidget(btnGetPlayerState);

    btnGetLog = new QPushButton;
    btnGetLog->setProperty("ssType", "progManageTool");
    connect(btnGetLog, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("Getting Log")+" ...");
            waitingDlg->show();
            auto card = gSelCards[0];
            auto reply = NetReq("http://"+card.ip+":2016/download?file=logs").timeout(120000).get();
            waitingDlg->connAbort(reply);
            connect(reply, &QNetworkReply::finished, this, [=] {
                waitingDlg->close();
                QString err = checkReply(reply);
                if(! err.isEmpty()) {
                    QMessageBox::critical(this, tr("Error"), err);
                    return;
                }
                QString logFile = QApplication::applicationDirPath()+"/card-log.txt";
                QFile file(logFile);
                if(! file.open(QIODevice::WriteOnly)) {
                    QMessageBox::critical(this, tr("Error"), "Open for Write Fail");
                    return;
                }
                file.write(reply->readAll());
                file.close();
                QProcess::execute("notepad", {logFile});
            });
        }
    });
    hBox->addWidget(btnGetLog);

    hBox->addStretch();
    hBox = new HBox(vBox);
    hBox->addWidget(lbTimingReboot = new QLabel);

    auto fdRebootTime = new QLineEdit;
    fdRebootTime->setMaximumWidth(60);
    hBox->addWidget(fdRebootTime);

    btnTimingRebootSet = new QPushButton;
    btnTimingRebootSet->setProperty("ssType", "progManageTool");
    connect(btnTimingRebootSet, &QPushButton::clicked, this, [=] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "SetTimingReboot");
        json.insert("_type", "SetTimingReboot");
        json.insert("time", fdRebootTime->text());
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("Setting Timing Reboot")+" ...");
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("Set Timing Reboot"))
            }
        }
    });
    hBox->addWidget(btnTimingRebootSet);

    btnTimingRebootGet = new QPushButton;
    btnTimingRebootGet->setProperty("ssType", "progManageTool");
    connect(btnTimingRebootGet, &QPushButton::clicked, this, [=] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "GetTimingReboot");
        json.insert("_type", "GetTimingReboot");
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("Getting Timing Reboot")+" ...");
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSingleGetReply
                waitingDlg->success();
                fdRebootTime->setText(json["time"].toString());
            });
        } else {
            foreach(auto card, gSelCards) {
                auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                auto cardId = card.id;
                connect(reply, &QNetworkReply::finished, this, [reply, cardId] {
                    QJsonDocument json;
                    QString err = checkReplyForJson(reply, &json);
                    if(err.isEmpty()) err = json["time"].toString();
                    gFdResInfo->append(cardId+" "+tr("Get Timing Reboot")+" "+err);
                });
            }
        }
    });
    hBox->addWidget(btnTimingRebootGet);

    hBox->addStretch();

    grpY50 = new QGroupBox;
    {
        auto hBox = new HBox(grpY50);

        auto fdY50Resolu = new QComboBox;
        auto dirs = QDir(QApplication::applicationDirPath()+"/y50 param").entryList(QDir::Dirs | QDir::NoDotAndDotDot);
        foreach(auto dir, dirs) fdY50Resolu->addItem(dir);
        fdY50Resolu->setMinimumWidth(160);
        hBox->addWidget(fdY50Resolu);

        btnY50Set = new QPushButton;
        btnY50Set->setProperty("ssType", "progManageTool");
        connect(btnY50Set, &QPushButton::clicked, this, [=] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            auto filePath = QApplication::applicationDirPath()+"/y50 param/"+fdY50Resolu->currentText()+"/rk_lcd_parameters";
            QFile qFile(filePath);
            if(! qFile.exists()) {
                QMessageBox::information(this, tr("Tip"), tr("File not exist"));
                return;
            }
            if(! qFile.open(QIODevice::ReadOnly)) {
                QMessageBox::information(this, tr("Tip"), tr("Cannot Open File")+": "+qFile.errorString()+"\n"+filePath);
                return;
            }
            auto fileData = qFile.readAll();
            qFile.close();

            auto Boundary = "----QtLedOK_.oOo._"+QUuid::createUuid().toByteArray(QUuid::WithoutBraces);
            QByteArray data;
            data.append("--").append(Boundary).append("\r\nContent-Disposition: form-data; name=\"rk_lcd_parameters\"; filename=\"rk_lcd_parameters\"\r\n\r\n").append(fileData).append("\r\n");
            data.append("--").append(Boundary).append("--\r\n");

            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("Uploading")+" ...");
                waitingDlg->show();
                NetReq req("http://"+gSelCards[0].ip+":2016/upload?type=update_display");
                auto reply = req.timeout(120000).type("multipart/form-data; boundary="+Boundary).post(data);
                waitingDlg->connAbort(reply);
                connect(reply, &QNetworkReply::finished, this, [=] {
                    QString err = checkReply(reply);
                    if(! err.isEmpty()) {
                        waitingDlg->close();
                        QMessageBox::critical(this, tr("Error"), err);
                        return;
                    }
                    waitingDlg->success();
                });
            } else {
                foreach(auto card, gSelCards) {
                    NetReq req("http://"+card.ip+":2016/upload?type=update_display");
                    auto reply = req.timeout(120000).type("multipart/form-data; boundary="+Boundary).post(data);
                    connect(reply, &QNetworkReply::finished, this, [=] {
                        QString err = checkReply(reply);
                        gFdResInfo->append(card.id+" "+tr("Update")+" "+(err.isEmpty()?tr("Success"):err));
                    });
                }
            }
        });
        hBox->addWidget(btnY50Set);
        hBox->addStretch();
    }
    vBox->addWidget(grpY50);


    hBox = new HBox(vBox);

    lbDisMode = new QLabel;
    hBox->addWidget(lbDisMode);

    fdDisMode = new QComboBox;
    fdDisMode->setSizeAdjustPolicy(QComboBox::AdjustToContents);
    fdDisMode->addItem("Full screen", 1);
    fdDisMode->addItem("Part", 2);
    hBox->addWidget(fdDisMode);

    btnDisModeSet = new QPushButton;
    btnDisModeSet->setProperty("ssType", "progManageTool");
    connect(btnDisModeSet, &QPushButton::clicked, this, [=] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController")+" ...");
            return;
        }
        QJsonObject json;
        json.insert("_id", "SetDisplayModel");
        json.insert("_type", "SetDisplayModel");
        json.insert("model", fdDisMode->currentData().toInt());
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("Set Display Mode"));
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("Set Display Mode"))
            }
        }
    });
    hBox->addWidget(btnDisModeSet);

    btnDisModeGet = new QPushButton;
    btnDisModeGet->setProperty("ssType", "progManageTool");
    connect(btnDisModeGet, &QPushButton::clicked, this, [=] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "GetDisplayModel");
        json.insert("_type", "GetDisplayModel");
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("Get Display Mode")+" ...");
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSingleGetReply
                waitingDlg->success();
                setCurrentData(fdDisMode, json["result"].toInt());
            });
        } else {
            foreach(auto card, gSelCards) {
                auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                auto cardId = card.id;
                connect(reply, &QNetworkReply::finished, this, [=] {
                    QJsonDocument json;
                    QString err = checkReplyForJson(reply, &json);
                    if(err.isEmpty()) {
                        auto result = json["result"].toInt();
                        int idx = fdDisMode->findData(result);
                        if(idx!=-1) err = fdDisMode->itemText(idx);
                        else err = QString::number(result);
                    }
                    gFdResInfo->append(cardId+" "+tr("Get Display Mode")+" "+err);
                });
            }
        }
    });
    hBox->addWidget(btnDisModeGet);
    hBox->addSpacing(40);

    lbScreenPos = new QLabel;
    hBox->addWidget(lbScreenPos);

    auto fdScreenPos = new QSpinBox;
    fdScreenPos->setRange(0, 99999);
    hBox->addWidget(fdScreenPos);

    lbScreenOff = new QLabel;
    hBox->addWidget(lbScreenOff);

    auto fdScreenOff = new QSpinBox;
    fdScreenOff->setRange(-9999, 99999);
    hBox->addWidget(fdScreenOff);

    btnScreenOffSet = new QPushButton;
    btnScreenOffSet->setProperty("ssType", "progManageTool");
    connect(btnScreenOffSet, &QPushButton::clicked, this, [=] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "ConfigScreen");
        json.insert("_type", "ConfigScreen");
        json.insert("index", fdScreenPos->value());
        json.insert("x", fdScreenOff->value());
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("Set Screen Offset")+" ...");
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("Set Screen Offset"))
            }
        }
    });
    hBox->addWidget(btnScreenOffSet);

    btnScreenOffGet = new QPushButton;
    btnScreenOffGet->setProperty("ssType", "progManageTool");
    connect(btnScreenOffGet, &QPushButton::clicked, this, [=] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "GetConfigScreen");
        json.insert("_type", "GetConfigScreen");
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("Get Screen Offset")+" ...");
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSingleGetReply
                waitingDlg->success();
                fdScreenPos->setValue(json["offsetNum"].toInt());
                fdScreenOff->setValue(json["offsetValue"].toInt());
            });
        } else {
            foreach(auto card, gSelCards) {
                auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                auto cardId = card.id;
                connect(reply, &QNetworkReply::finished, this, [=] {
                    QJsonDocument json;
                    QString err = checkReplyForJson(reply, &json);
                    if(err.isEmpty()) {
                        err = lbScreenPos->text()+" "+QString::number(json["offsetNum"].toInt())+". "+lbScreenOff->text()+" "+QString::number(json["offsetValue"].toInt());
                    }
                    gFdResInfo->append(cardId+" "+tr("Get Screen Offset")+" "+err);
                });
            }
        }
    });
    hBox->addWidget(btnScreenOffGet);
    hBox->addStretch();

    hBox = new HBox(vBox);

    btnLedSet = new QPushButton;
    btnLedSet->setMinimumHeight(30);
    btnLedSet->setProperty("ssType", "progManageTool");
    connect(btnLedSet, &QPushButton::clicked, btnLedSet, [] {
        QFileInfo file("LedSet4.0/LedSet4.0.exe");
        if(file.exists()) QProcess::startDetached(file.absoluteFilePath(), QStringList());
    });
    hBox->addWidget(btnLedSet);
    hBox->addStretch();

#ifndef Q_OS_WIN
    btnLedSet->setVisible(false);
#endif

    auto line = new QFrame;
    line->setFrameShape(QFrame::HLine);
    line->setFrameShadow(QFrame::Sunken);
    vBox->addWidget(line);

    hBox = new HBox(vBox);
    btnBindTaxiIc = new QPushButton;
    btnBindTaxiIc->setProperty("ssType", "progManageTool");
    connect(btnBindTaxiIc, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QString icFile = QFileDialog::getOpenFileName(this, "open file dialog", "", tr("indentity voucher (*.ic)"));
        if(icFile.isEmpty()) return;
        QFile file(icFile);
        if(! file.open(QIODevice::ReadOnly)) {
            QMessageBox::information(this, tr("Tip"), tr("Open file Failed"));
            return;
        }
        auto data = file.readAll();
        file.close();
        QJsonParseError jsonErr;
        QJsonDocument icJson = QJsonDocument::fromJson(data, &jsonErr);
        if(jsonErr.error != QJsonParseError::NoError) {
            QMessageBox::information(this, tr("Tip"), "JsonError "+jsonErr.errorString()+"\n"+data);
            return;
        }
        QJsonObject jsonCommand;
        jsonCommand.insert("action", "BindAccount");
        jsonCommand.insert("accountIdToken", icJson["account_id_token"]);
        jsonCommand.insert("server", icJson["taxiServerURL"]);
        jsonCommand.insert("tlsServer", icJson["taxiServerTLSURL"]);
        QJsonObject json;
        json.insert("action", "InvokeTaxiAppFunction");
        json.insert("jsonCommand", jsonCommand);
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("InvokeTaxiAppFunction"));
            waitingDlg->show();
            auto reply = NetReq("http://"+gSelCards[0].ip+":3000").timeout(120000).post(json);
            waitingDlg->connAbort(reply);
            connect(reply, &QNetworkReply::finished, this, [=] {
                QString err = checkReply(reply);
                if(! err.isEmpty()) {
                    waitingDlg->close();
                    QMessageBox::critical(this, tr("Error"), err);
                    return;
                }
                auto data = reply->readAll();
                QJsonParseError jsonErr;
                QJsonDocument json = QJsonDocument::fromJson(data, &jsonErr);
                if(jsonErr.error != QJsonParseError::NoError) {
                    waitingDlg->close();
                    QMessageBox::critical(this, tr("Error"), "JsonError "+jsonErr.errorString()+"\n"+data);
                    return;
                }
                if(json["result"].toString() != "true") {
                    waitingDlg->close();
                    QMessageBox::critical(this, tr("Error"), data);
                    return;
                }
                waitingDlg->success();
            });
        } else {
            foreach(auto card, gSelCards) {
                auto reply = NetReq("http://"+card.ip+":3000").timeout(120000).post(json);
                auto cardId = card.id;
                connect(reply, &QNetworkReply::finished, this, [reply, cardId] {
                    QString err = checkReply(reply);
                    if(! err.isEmpty()) {
                        gFdResInfo->append(cardId+" "+tr("InvokeTaxiAppFunction")+" "+tr("Error")+" "+err);
                        return;
                    }
                    auto data = reply->readAll();
                    QJsonParseError jsonErr;
                    QJsonDocument json = QJsonDocument::fromJson(data, &jsonErr);
                    if(jsonErr.error != QJsonParseError::NoError) {
                        gFdResInfo->append(cardId+" "+tr("InvokeTaxiAppFunction")+" "+tr("Error")+" JsonError "+jsonErr.errorString()+"\n"+data);
                        return;
                    }
                    if(json["result"].toString() != "true") {
                        gFdResInfo->append(cardId+" "+tr("InvokeTaxiAppFunction")+" "+tr("Error")+" "+data);
                        return;
                    }
                    gFdResInfo->append(cardId+" "+tr("InvokeTaxiAppFunction")+" "+QCoreApplication::translate("Def","Success"));
                });
            }
        }
    });
    hBox->addWidget(btnBindTaxiIc);
    hBox->addStretch();


    grpHighForBusy = new QGroupBox;
    grpHighForBusy->setVisible(false);
    hBox = new HBox(grpHighForBusy);

    fdHighForBusy = new QRadioButton;
    fdHighForBusy->setChecked(true);
    hBox->addWidget(fdHighForBusy);

    fdTopLevelLH = new QRadioButton;
    hBox->addWidget(fdTopLevelLH);

    btnHighForBusySet = new QPushButton;
    btnHighForBusySet->setProperty("ssType", "progManageTool");
    connect(btnHighForBusySet, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "SetHighForBusy");
        json.insert("_type", "SetHighForBusy");
        json.insert("busyState", fdTopLevelLH->isChecked() ? 0 : 1);
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("SetHighForBusy")+" ...");
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("SetHighForBusy"))
            }
        }
    });
    hBox->addWidget(btnHighForBusySet);

    btnGetTopLevel = new QPushButton;
    btnGetTopLevel->setProperty("ssType", "progManageTool");
    connect(btnGetTopLevel, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "GetStateForBusy");
        json.insert("_type", "GetStateForBusy");
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("GetStateForBusy")+" ...");
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [this, reply, waitingDlg] {
                Def_CtrlSingleGetReply
                waitingDlg->success();
                if(json["result"].toInt()==0) fdTopLevelLH->setChecked(true);
                else fdHighForBusy->setChecked(true);
            });
        } else {
            foreach(auto card, gSelCards) {
                auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                auto cardId = card.id;
                connect(reply, &QNetworkReply::finished, this, [reply, cardId] {
                    QJsonDocument json;
                    QString err = checkReplyForJson(reply, &json);
                    if(err.isEmpty()) err = json["result"].toInt() ? tr("Service:High  Out of service:Low") : tr("Service:Low  Out of service:High");
                    gFdResInfo->append(cardId+" "+tr("GetStateForBusy")+" "+err);
                });
            }
        }
    });
    hBox->addWidget(btnGetTopLevel);

    hBox->addStretch();

    vBox->addWidget(grpHighForBusy);

    grpMinMaxBrightness = new QGroupBox();
    grpMinMaxBrightness->setVisible(false);
    hBox = new HBox(grpMinMaxBrightness);

    lbMinBright = new QLabel();
    hBox->addWidget(lbMinBright);

    fdMinBright = new QLineEdit("1");
    fdMinBright->setPlaceholderText("1");
    fdMinBright->setMaximumWidth(50);
    hBox->addWidget(fdMinBright);
    hBox->addWidget(new QLabel("%"));

    btnMinBrightSet = new QPushButton;
    btnMinBrightSet->setProperty("ssType", "progManageTool");
    connect(btnMinBrightSet, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "SetMinBrightness");
        json.insert("_type", "SetMinBrightness");
        json.insert("minBrightnessPercentage", fdMinBright->text().toInt());
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("SetMinBrightness"));
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("SetMinBrightness"))
            }
        }
    });
    hBox->addWidget(btnMinBrightSet);

    btnMinBrightGet = new QPushButton();
    btnMinBrightGet->setProperty("ssType", "progManageTool");
    connect(btnMinBrightGet, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "GetMinBrightness");
        json.insert("_type", "GetMinBrightness");
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("GetMinBrightness")+" ...");
            Def_CtrlReqPre
            auto brightLevel = card.BrightnessLevel;
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSingleGetReply
                waitingDlg->success();
                auto value = json["minBrightnessPercentage"].toInt(-1);
                if(value==-1) value = qRound(json["brightness"].toInt() * 100.0 / brightLevel);
                fdMinBright->setText(QString::number(value));
            });
        } else {
            foreach(auto card, gSelCards) {
                auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                auto cardId = card.id;
                auto brightLevel = card.BrightnessLevel;
                connect(reply, &QNetworkReply::finished, this, [=] {
                    QJsonDocument json;
                    QString err = checkReplyForJson(reply, &json);
                    if(err.isEmpty()) {
                        auto value = json["minBrightnessPercentage"].toInt(-1);
                        if(value==-1) value = qRound(json["brightness"].toInt() * 100.0 / brightLevel);
                        err = QString::number(value)+"%";
                    }
                    gFdResInfo->append(cardId+" "+tr("GetMinBrightness")+" "+err);
                });
            }
        }
    });
    hBox->addWidget(btnMinBrightGet);

    hBox->addSpacing(10);

    label_3 = new QLabel();
    hBox->addWidget(label_3);

    fdMaxBright = new QLineEdit("100");
    fdMaxBright->setPlaceholderText("100");
    fdMaxBright->setMaximumWidth(50);
    hBox->addWidget(fdMaxBright);
    hBox->addWidget(new QLabel("%"));

    btnMaxBrightSet = new QPushButton;
    btnMaxBrightSet->setProperty("ssType", "progManageTool");
    connect(btnMaxBrightSet, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "SetMaxBrightness");
        json.insert("_type", "SetMaxBrightness");
        json.insert("maxBrightnessPercentage", fdMaxBright->text().toInt());
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("SetMaxBrightness"));
            Def_CtrlReqPre
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSetReqAfter
            });
        } else {
            foreach(auto card, gSelCards) {
                Def_CtrlSetMulti(tr("SetMaxBrightness"))
            }
        }
    });
    hBox->addWidget(btnMaxBrightSet);

    btnMaxBrightGet = new QPushButton;
    btnMaxBrightGet->setProperty("ssType", "progManageTool");
    connect(btnMaxBrightGet, &QPushButton::clicked, this, [this] {
        if(gSelCards.isEmpty()) {
            QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
            return;
        }
        QJsonObject json;
        json.insert("_id", "GetMaxBrightness");
        json.insert("_type", "GetMaxBrightness");
        if(gSelCards.count() == 1) {
            auto waitingDlg = new WaitingDlg(this, tr("GetMaxBrightness")+" ...");
            Def_CtrlReqPre
            auto brightLevel = card.BrightnessLevel;
            connect(reply, &QNetworkReply::finished, this, [=] {
                Def_CtrlSingleGetReply
                waitingDlg->success();
                auto value = json["maxBrightnessPercentage"].toInt(-1);
                if(value==-1) value = qRound(json["brightness"].toInt() * 100.0 / brightLevel);
                fdMaxBright->setText(QString::number(value));
            });
        } else {
            foreach(auto card, gSelCards) {
                auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                auto cardId = card.id;
                auto brightLevel = card.BrightnessLevel;
                connect(reply, &QNetworkReply::finished, this, [=] {
                    QJsonDocument json;
                    QString err = checkReplyForJson(reply, &json);
                    if(err.isEmpty()) {
                        auto value = json["maxBrightnessPercentage"].toInt(-1);
                        if(value==-1) value = qRound(json["brightness"].toInt() * 100.0 / brightLevel);
                        err = QString::number(value)+"%";
                    }
                    gFdResInfo->append(cardId+" "+tr("GetMaxBrightness")+" "+err);
                });
            }
        }
    });
    hBox->addWidget(btnMaxBrightGet);

    hBox->addStretch();
    vBox->addWidget(grpMinMaxBrightness);

    vBox->addSpacing(20);

    grpBoxHiddenSettings = new QGroupBox;
    grpBoxHiddenSettings->setVisible(false);
    {
        auto vBox = new QVBoxLayout(grpBoxHiddenSettings);
        vBox->setContentsMargins(6,6,6,6);

        hBox = new HBox(vBox);

        btnSetBack = new QPushButton;
        btnSetBack->setProperty("ssType", "progManageTool");
        connect(btnSetBack, &QPushButton::clicked, this, [this] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QColorDialog colorDlg(this);
            colorDlg.setOption(QColorDialog::DontUseNativeDialog);

            if(colorDlg.exec() != QColorDialog::Accepted) return;
            QColor color = colorDlg.selectedColor();
            if(! color.isValid()) return;
            QJsonObject json;
            json.insert("_id", "SetWallpaper");
            json.insert("_type", "SetWallpaper");
            json.insert("rgb", color.name());
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("Setting Wallpaper")+" ...");
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSetReqAfter
                });
            } else {
                foreach(auto card, gSelCards) {
                    Def_CtrlSetMulti(tr("Set Wallpaper"))
                }
            }
        });
        hBox->addWidget(btnSetBack);

        btnSysUpd = new QPushButton;
        btnSysUpd->setProperty("ssType", "progManageTool");
        connect(btnSysUpd, &QPushButton::clicked, this, [this] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QJsonObject json;
            json.insert("_id", "SystemUpdate");
            json.insert("_type", "SystemUpdate");
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("System Updating")+" ...");
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSetReqAfter
                });
            } else {
                foreach(auto card, gSelCards) {
                    Def_CtrlSetMulti(tr("System Update"))
                }
            }
        });
        hBox->addWidget(btnSysUpd);

        hBox->addSpacing(20);

        btnMcuUpd = new QPushButton;
        btnMcuUpd->setProperty("ssType", "progManageTool");
        connect(btnMcuUpd, &QPushButton::clicked, this, [this] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            auto filePath = QFileDialog::getOpenFileName(this, "Open File", QString(), ".bin file (*.bin)");
            if(filePath.isEmpty()) return;
            QFile qFile(filePath);
            if(! qFile.exists()) {
                QMessageBox::information(this, tr("Tip"), tr("File not exist"));
                return;
            }
            if(! qFile.open(QIODevice::ReadOnly)) {
                QMessageBox::information(this, tr("Tip"), tr("Cannot Open File")+": "+qFile.errorString()+"\n"+filePath);
                return;
            }
            auto fileData = qFile.readAll();
            qFile.close();

            auto nameBytes = QFileInfo(filePath).fileName().toUtf8();
            auto Boundary = "----QtLedOK_.oOo._"+QUuid::createUuid().toByteArray(QUuid::WithoutBraces);
            QByteArray data;
            data.append("--").append(Boundary).append("\r\nContent-Disposition: form-data; name=\"").append(nameBytes).append("\"; filename=\"").append(nameBytes).append("\"\r\n\r\n").append(fileData).append("\r\n");
            data.append("--").append(Boundary).append("--\r\n");

            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("MCU Uploading")+" ...");
                waitingDlg->show();
                NetReq req("http://"+gSelCards[0].ip+":2016/upload?type=mcu_update");
                auto reply = req.timeout(120000).type("multipart/form-data; boundary="+Boundary).post(data);
                waitingDlg->connAbort(reply);
                connect(reply, &QNetworkReply::finished, this, [=] {
                    QString err = checkReply(reply);
                    if(! err.isEmpty()) {
                        waitingDlg->close();
                        QMessageBox::critical(this, tr("Error"), err);
                        return;
                    }
                    waitingDlg->success();
                });
            } else {
                foreach(auto card, gSelCards) {
                    NetReq req("http://"+card.ip+":2016/upload?type=mcu_update");
                    auto reply = req.timeout(120000).type("multipart/form-data; boundary="+Boundary).post(data);
                    connect(reply, &QNetworkReply::finished, this, [=] {
                        QString err = checkReply(reply);
                        gFdResInfo->append(card.id+" "+tr("Update MCU")+" "+(err.isEmpty()?tr("Success"):err));
                    });
                }
            }
        });
        hBox->addWidget(btnMcuUpd);

        btnMcuGet = new QPushButton;
        btnMcuGet->setProperty("ssType", "progManageTool");
        connect(btnMcuGet, &QPushButton::clicked, this, [this] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QJsonObject json;
            json.insert("_id", "SelectMCUVersion");
            json.insert("_type", "SelectMCUVersion");
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("Getting MCU Version")+" ...");
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSingleGetReply
                    waitingDlg->close();
                    QMessageBox::information(this, tr("Tip"), tr("MCU Version")+": "+json["mcuVersion"].toString());
                });
            } else {
                foreach(auto card, gSelCards) {
                    auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                    auto cardId = card.id;
                    connect(reply, &QNetworkReply::finished, this, [reply, cardId] {
                        QJsonDocument json;
                        QString err = checkReplyForJson(reply, &json);
                        if(err.isEmpty()) err = json["mcuVersion"].toString();
                        gFdResInfo->append(cardId+" "+tr("MCU Version")+" "+err);
                    });
                }
            }
        });
        hBox->addWidget(btnMcuGet);

        hBox->addSpacing(20);

        btnPlayerBackSet = new QPushButton;
        btnPlayerBackSet->setProperty("ssType", "progManageTool");
        connect(btnPlayerBackSet, &QPushButton::clicked, this, [this] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QString file = QFileDialog::getOpenFileName(this, tr("Select File"), gFileHome, EPhoto::filters());
            if(file.isEmpty()) return;
            QFileInfo info(file);
            if(! info.isFile()) return;
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("Setting player background")+" ...");
                waitingDlg->setWindowFlag(Qt::WindowCloseButtonHint, 0);
                waitingDlg->show();
                auto thread = new PlayerBackSendThread(file, gSelCards[0].ip);
                connect(thread, &PlayerBackSendThread::emErr, this, [=](QString err) {
                    if(err.isEmpty()) waitingDlg->success();
                    else {
                        waitingDlg->close();
                        QMessageBox::critical(this, tr("Error"), err);
                    }
                });
                thread->start();
            } else {
                foreach(auto card, gSelCards) {
                    auto thread = new PlayerBackSendThread(file, card.ip);
                    auto cardId = card.id;
                    connect(thread, &PlayerBackSendThread::emErr, this, [cardId](QString err) {
                        gFdResInfo->append(cardId+" "+tr("Set player background")+" "+(err.isEmpty() ? tr("Success") : err));
                    });
                    thread->start();
                }
            }
        });
        hBox->addWidget(btnPlayerBackSet);

        btnPlayerBackClear = new QPushButton;
        btnPlayerBackClear->setProperty("ssType", "progManageTool");
        connect(btnPlayerBackClear, &QPushButton::clicked, this, [this] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("Clearing player background")+" ...");
                waitingDlg->show();
                auto card = gSelCards[0];
                auto tcp = new QTcpSocket();
                auto timer = new QTimer(tcp);
                timer->setSingleShot(true);
                connect(timer, &QTimer::timeout, tcp, [=] {
                    waitingDlg->close();
                    tcp->abort();
                    tcp->deleteLater();
                    QMessageBox::critical(this, tr("Tip"), tr("Clear player background")+" "+tr("Timeout"));
                });
                connect(waitingDlg, &WaitingDlg::rejected, tcp, [=] {
                    timer->stop();
                    tcp->abort();
                    tcp->deleteLater();
                });
                connect(tcp, &QTcpSocket::connected, tcp, [tcp, timer] {
                    timer->stop();
                    tcp->write("{\"_type\":\"DelBackImg\",\"_id\":\"DelBackImg\",\"zVer\":\"xixun1\"}");
                    timer->start(10000);
                });
                connect(tcp, &QTcpSocket::readyRead, tcp, [=] {
                    timer->stop();
                    auto resp = tcp->readAll();
                    tcp->close();
                    tcp->deleteLater();
                    QJsonParseError parseErr;
                    QJsonDocument json = QJsonDocument::fromJson(resp, &parseErr);
                    if(parseErr.error != QJsonParseError::NoError) {
                        waitingDlg->close();
                        QMessageBox::critical(this, tr("Tip"), parseErr.errorString());
                    } else if(! json["success"].toBool()) {
                        waitingDlg->close();
                        QMessageBox::critical(this, tr("Tip"), tr("Clear player background")+" "+tr("Failed"));
                    } else waitingDlg->success();
                });
                connect(tcp, &QTcpSocket::errorOccurred, tcp, [=](QAbstractSocket::SocketError err) {
                    timer->stop();
                    tcp->close();
                    tcp->deleteLater();
                    waitingDlg->close();
                    QMessageBox::critical(this, tr("Tip"), QString(socketErrKey(err))+" ("+QString::number(err)+") "+tcp->errorString());
                });
                tcp->connectToHost(card.ip, 3333);
                timer->start(10000);
            } else {
                foreach(auto card, gSelCards) {
                    auto tcp = new QTcpSocket();
                    auto timer = new QTimer(tcp);
                    timer->setSingleShot(true);
                    auto cardId = card.id;
                    connect(timer, &QTimer::timeout, tcp, [tcp, cardId] {
                        tcp->abort();
                        tcp->deleteLater();
                        gFdResInfo->append(cardId+" "+tr("Clear player background")+" "+tr("Timeout"));
                    });
                    connect(tcp, &QTcpSocket::connected, tcp, [tcp, timer] {
                        timer->stop();
                        tcp->write("{\"_type\":\"DelBackImg\",\"_id\":\"DelBackImg\",\"zVer\":\"xixun1\"}");
                        timer->start(10000);
                    });
                    connect(tcp, &QTcpSocket::readyRead, tcp, [tcp, timer, cardId] {
                        timer->stop();
                        auto resp = tcp->readAll();
                        tcp->close();
                        tcp->deleteLater();
                        QJsonParseError parseErr;
                        QJsonDocument json = QJsonDocument::fromJson(resp, &parseErr);
                        if(parseErr.error != QJsonParseError::NoError) gFdResInfo->append(cardId+" "+tr("Clear player background")+" "+parseErr.errorString());
                        else if(! json["success"].toBool()) gFdResInfo->append(cardId+" "+tr("Clear player background")+" "+tr("Failed"));
                        else gFdResInfo->append(cardId+" "+tr("Clear player background")+" "+tr("Success"));
                    });
                    connect(tcp, &QTcpSocket::errorOccurred, tcp, [tcp, timer, cardId](QAbstractSocket::SocketError err) {
                        timer->stop();
                        tcp->close();
                        tcp->deleteLater();
                        gFdResInfo->append(cardId+" "+tr("Clear player background")+" "+QMetaEnum::fromType<QAbstractSocket::SocketError>().valueToKey(err)+" ("+QString::number(err)+") "+tcp->errorString());
                    });
                    tcp->connectToHost(card.ip, 3333);
                    timer->start(10000);
                }
            }
        });
        hBox->addWidget(btnPlayerBackClear);

        hBox->addStretch();

        auto line = new QFrame;
        line->setFrameStyle(QFrame::HLine | QFrame::Sunken);
        vBox->addWidget(line);


        grpM80 = new QGroupBox;
        {
            auto hBox = new HBox(grpM80);

            fdM80Resolu = new QComboBox;
            fdM80Resolu->setMinimumWidth(160);
            hBox->addWidget(fdM80Resolu);

            btnM80Set = new QPushButton();
            btnM80Set->setProperty("ssType", "progManageTool");
            connect(btnM80Set, &QPushButton::clicked, this, [this] {
                if(gSelCards.isEmpty()) {
                    QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                    return;
                }
                QJsonObject json;
                json.insert("_id", "SetSpecialResolution");
                json.insert("_type", "SetSpecialResolution");
                json.insert("displayResolution", fdM80Resolu->currentText());	//显示分辨率
                json.insert("totalResolution", fdM80Resolu->currentData().toString());	//显示分辨率
                if(gSelCards.count() == 1) {
                    auto waitingDlg = new WaitingDlg(this, tr("SetSpecialResolution"));
                    Def_CtrlReqPre
                    connect(reply, &QNetworkReply::finished, this, [=] {
                        Def_CtrlSetReqAfter
                    });
                } else {
                    foreach(auto card, gSelCards) {
                        Def_CtrlSetMulti(tr("SetSpecialResolution"))
                    }
                }
            });
            hBox->addWidget(btnM80Set);

            btnM80Refresh = new QPushButton();
            btnM80Refresh->setProperty("ssType", "progManageTool");
            connect(btnM80Refresh, &QPushButton::clicked, this, [this] {
                if(gSelCards.isEmpty()) {
                    QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                    return;
                }
                QJsonObject json;
                json.insert("_id", "GetSpecialResolution");
                json.insert("_type", "GetSpecialResolution");
                if(gSelCards.count() == 1) {
                    auto waitingDlg = new WaitingDlg(this, tr("GetSpecialResolution"));
                    Def_CtrlReqPre
                    connect(reply, &QNetworkReply::finished, this, [this, reply, waitingDlg] {
                        Def_CtrlSingleGetReply
                        waitingDlg->success();
                        fdM80Resolu->setCurrentText(json["displayResolution"].toString());
                    });
                } else {
                    foreach(auto card, gSelCards) {
                        auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                        auto cardId = card.id;
                        connect(reply, &QNetworkReply::finished, this, [reply, cardId] {
                            QJsonDocument json;
                            QString err = checkReplyForJson(reply, &json);
                            if(err.isEmpty()) err = tr("totalResolution")+"["+json["totalResolution"].toString()+"], "+tr("strCurDisplayResolution")+"["+json["displayResolution"].toString()+"]";
                            gFdResInfo->append(cardId+" "+tr("GetSpecialResolution")+" "+err);
                        });
                    }
                }
            });
            hBox->addWidget(btnM80Refresh);

            btnM80Restore = new QPushButton();
            btnM80Restore->setProperty("ssType", "progManageTool");
            connect(btnM80Restore, &QPushButton::clicked, this, [this] {
                if(gSelCards.isEmpty()) {
                    QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                    return;
                }
                QJsonObject json;
                json.insert("_id", "CleanDisplayScreenSize");
                json.insert("_type", "CleanDisplayScreenSize");
                if(gSelCards.count() == 1) {
                    auto waitingDlg = new WaitingDlg(this, tr("CleanDisplayScreenSize"));
                    Def_CtrlReqPre
                    connect(reply, &QNetworkReply::finished, this, [=] {
                        Def_CtrlSetReqAfter
                    });
                } else {
                    foreach(auto card, gSelCards) {
                        Def_CtrlSetMulti(tr("CleanDisplayScreenSize"))
                    }
                }
            });
            hBox->addWidget(btnM80Restore);
            hBox->addStretch();
        }
        vBox->addWidget(grpM80);


        hBox = new HBox(vBox);

        lbRotate = new QLabel;
        hBox->addWidget(lbRotate);
        hBox->addSpacing(20);

        auto fdDeg0 = new QRadioButton("0°");
        hBox->addWidget(fdDeg0);
        auto fdDeg90 = new QRadioButton("90°");
        hBox->addWidget(fdDeg90);
        auto fdDeg180 = new QRadioButton("180°");
        hBox->addWidget(fdDeg180);
        auto fdDeg270 = new QRadioButton("270°");
        hBox->addWidget(fdDeg270);
        auto btnGrp = new QButtonGroup(hBox);
        btnGrp->addButton(fdDeg0, 0);
        btnGrp->addButton(fdDeg90, 1);
        btnGrp->addButton(fdDeg180, 2);
        btnGrp->addButton(fdDeg270, 3);

        btnRotateSet = new QPushButton;
        btnRotateSet->setProperty("ssType", "progManageTool");
        connect(btnRotateSet, &QPushButton::clicked, this, [=] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            auto id = btnGrp->checkedId();
            QJsonObject json;
            json.insert("_id", "SetScreenRotation");
            json.insert("_type", "SetScreenRotation");
            json.insert("rotation", id);
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("SetScreenRotation"));
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSetReqAfter
                });
            } else {
                foreach(auto card, gSelCards) {
                    Def_CtrlSetMulti(tr("SetScreenRotation"))
                }
            }
        });
        hBox->addWidget(btnRotateSet);

        btnRotateGet = new QPushButton;
        btnRotateGet->setProperty("ssType", "progManageTool");
        connect(btnRotateGet, &QPushButton::clicked, this, [=] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QJsonObject json;
            json.insert("_id", "GetScreenRotation");
            json.insert("_type", "GetScreenRotation");
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("GetScreenRotation")+" ...");
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSingleGetReply
                    waitingDlg->success();
                    auto btn = btnGrp->button(json["rotation"].toInt());
                    if(btn) btn->setChecked(true);
                });
            } else {
                foreach(auto card, gSelCards) {
                    auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                    connect(reply, &QNetworkReply::finished, this, [=] {
                        QJsonDocument json;
                        QString err = checkReplyForJson(reply, &json);
                        if(err.isEmpty()) {
                            auto rotation = json["rotation"].toInt();
                            auto btn = btnGrp->button(rotation);
                            err = btn ? btn->text() : QString::number(rotation);
                        }
                        gFdResInfo->append(card.id+" "+tr("GetScreenRotation")+" "+err);
                    });
                }
            }
        });
        hBox->addWidget(btnRotateGet);

        hBox->addStretch();


        hBox = new HBox(vBox);

        lbBaudCfg = new QLabel;
        hBox->addWidget(lbBaudCfg);

        line = new QFrame;
        line->setFrameStyle(QFrame::HLine | QFrame::Sunken);
        hBox->addWidget(line, 1);

        hBox = new HBox(vBox);

        lbBaudModel = new QLabel;
        hBox->addWidget(lbBaudModel);

        auto fdBaudDevMod = new QComboBox;
        fdBaudDevMod->addItems({
            "M5x/M6x",
            "M7x/Y7x",
            "E2x/Y6x",
            "L20"
        });
        hBox->addWidget(fdBaudDevMod);

        lbUart = new QLabel;
        hBox->addWidget(lbUart);

        auto fdUart = new QComboBox;
        fdUart->setEditable(true);
        fdUart->setMinimumWidth(140);
        fdUart->setSizeAdjustPolicy(QComboBox::AdjustToContents);
        fdUart->addItems({
            "/dev/ttyS3",
            "/dev/ttyS4",
            "/dev/ttyS5"
        });
        hBox->addWidget(fdUart);

        connect(fdBaudDevMod, &QComboBox::currentTextChanged, fdUart, [fdUart](const QString &text) {
            fdUart->clear();
            if(text.startsWith("M5x")) fdUart->addItems({
                "/dev/ttyS3",
                "/dev/ttyS4",
                "/dev/ttyS5"
            });
            else if(text.startsWith("M7x")) fdUart->addItem("/dev/ttyS4");
            else if(text.startsWith("E2x")) fdUart->addItems({
                "/dev/ttysWK0",
                "/dev/ttysWK1",
                "/dev/ttysWK2",
                "/dev/ttysWK3",
                "/dev/tty232",
                "/dev/tty485",
                "/dev/ttyTTL"
            });
            else if(text.startsWith("L20")) fdUart->addItems({
                "/dev/ttyS0",
                "/dev/ttyS1",
                "/dev/ttyS4"
            });
        });

        lbBaud = new QLabel;
        hBox->addWidget(lbBaud);

        auto fdBaud = new QLineEdit;
        fdBaud->setMaximumWidth(70);
        hBox->addWidget(fdBaud);

        btnBaudSet = new QPushButton;
        btnBaudSet->setProperty("ssType", "progManageTool");
        connect(btnBaudSet, &QPushButton::clicked, this, [this, fdUart, fdBaud] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QJsonObject json;
            json.insert("_id", "SetBaudRateByCardType");
            json.insert("_type", "SetBaudRateByCardType");
            json.insert("uartName", fdUart->currentText());
            json.insert("baud", fdBaud->text().toInt());
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("Setting Baud Rate")+" ...");
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSetReqAfter
                });
            } else {
                foreach(auto card, gSelCards) {
                    Def_CtrlSetMulti(tr("Set Baud Rate"))
                }
            }
        });
        hBox->addWidget(btnBaudSet);

        btnBaudGet = new QPushButton;
        btnBaudGet->setProperty("ssType", "progManageTool");
        connect(btnBaudGet, &QPushButton::clicked, this, [this, fdUart, fdBaud] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QJsonObject json;
            json.insert("_id", "GetBaudRateByCardType");
            json.insert("_type", "GetBaudRateByCardType");
            json.insert("uartName", fdUart->currentText());
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("Getting Baud Rate")+" ...");
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSingleGetReply
                    waitingDlg->success();
                    fdBaud->setText(QString::number(json["baud"].toInt()));
                });
            } else {
                foreach(auto card, gSelCards) {
                    auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                    auto cardId = card.id;
                    connect(reply, &QNetworkReply::finished, this, [reply, cardId] {
                        QJsonDocument json;
                        QString err = checkReplyForJson(reply, &json);
                        gFdResInfo->append(cardId+" "+tr("Get Baud Rate")+" "+(err.isEmpty()?QString::number(json["baud"].toInt()):err));
                    });
                }
            }
        });
        hBox->addWidget(btnBaudGet);

        hBox->addStretch();


        fdIsOpenADB = new QCheckBox;
        connect(fdIsOpenADB, &QCheckBox::toggled, this, [this](bool checked) {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QJsonObject json;
            json.insert("_id", "OpenAdb");
            json.insert("_type", "OpenAdb");
            json.insert("open", checked);
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("OpenAdb")+" ...");
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSetReqAfter
                });
            } else {
                foreach(auto card, gSelCards) {
                    Def_CtrlSetMulti(tr("OpenAdb"))
                }
            }
        });
        vBox->addWidget(fdIsOpenADB);

        hBox = new HBox(vBox);

        lbCustomJson = new QLabel;
        hBox->addWidget(lbCustomJson);

        hBox->addSpacing(40);

        btnSendCustomJson = new QPushButton;
        btnSendCustomJson->setProperty("ssType", "progManageTool");
        connect(btnSendCustomJson, &QPushButton::clicked, this, [=] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            auto text = fdCustomJson->toPlainText().toUtf8();
            if(text.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("Text is empty"));
                return;
            }
            QJsonParseError jsonErr;
            QJsonDocument json = QJsonDocument::fromJson(text, &jsonErr);
            if(jsonErr.error != QJsonParseError::NoError) {
                QMessageBox::information(this, tr("Tip"), tr("Json Parse Error")+jsonErr.errorString());
                return;
            }
            if(! json.isObject()) {
                QMessageBox::information(this, tr("Tip"), tr("Json isn't an Object"));
                return;
            }
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("AliIotSetting"));
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSetReqAfter
                });
            } else {
                foreach(auto card, gSelCards) {
                    Def_CtrlSetMulti(tr("AliIotSetting"))
                }
            }
        });
        hBox->addWidget(btnSendCustomJson);

        hBox->addStretch();

        fdCustomJson = new QTextEdit;
        fdCustomJson->setMinimumHeight(120);
        vBox->addWidget(fdCustomJson);


        vBox->addWidget(new QLabel(tr("Traffic screen settings")));

        hBox = new HBox(vBox);

        lbTraficProtocol = new QLabel;
        lbTraficProtocol->setMinimumWidth(100);
        hBox->addWidget(lbTraficProtocol);

        auto fdTraficProtocol = new QComboBox;
        fdTraficProtocol->addItem("三思协议", 1);
        hBox->addWidget(fdTraficProtocol);

        btnTraficProtSet = new QPushButton;
        btnTraficProtSet->setProperty("ssType", "progManageTool");
        connect(btnTraficProtSet, &QPushButton::clicked, this, [this, fdTraficProtocol] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QJsonObject json;
            json.insert("_id", "SetProtocolType");
            json.insert("_type", "SetProtocolType");
            json.insert("protocolType", fdTraficProtocol->currentData().toInt());
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("Setting protocol ..."));
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSetReqAfter
                });
            } else {
                foreach(auto card, gSelCards) {
                    Def_CtrlSetMulti(tr("Set protocol"))
                }
            }
        });
        hBox->addWidget(btnTraficProtSet);

        btnTraficProtGet = new QPushButton;
        btnTraficProtGet->setProperty("ssType", "progManageTool");
        connect(btnTraficProtGet, &QPushButton::clicked, this, [this, fdTraficProtocol] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QJsonObject json;
            json.insert("_id", "GetProtocolType");
            json.insert("_type", "GetProtocolType");
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("Getting protocol ..."));
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSingleGetReply
                    waitingDlg->success();
                    setCurrentData(fdTraficProtocol, json["protocolType"].toInt());
                });
            } else {
                foreach(auto card, gSelCards) {
                    auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                    auto cardId = card.id;
                    connect(reply, &QNetworkReply::finished, this, [reply, cardId, fdTraficProtocol] {
                        QJsonDocument json;
                        QString err = checkReplyForJson(reply, &json);
                        if(err.isEmpty()) {
                            auto protocolType = json["protocolType"].toInt();
                            int idx = fdTraficProtocol->findData(protocolType);
                            if(idx!=-1) err = fdTraficProtocol->itemText(idx);
                            else err = QString::number(protocolType);
                        }
                        gFdResInfo->append(cardId+" "+tr("Get protocol")+" "+err);
                    });
                }
            }
        });
        hBox->addWidget(btnTraficProtGet);
        hBox->addStretch();

        hBox = new HBox(vBox);

        lbCardMode = new QLabel;
        lbCardMode->setMinimumWidth(100);
        hBox->addWidget(lbCardMode);

        fdServerType = new QComboBox;
        fdServerType->setSizeAdjustPolicy(QComboBox::AdjustToContents);
        fdServerType->addItem("服务端", 1);
        fdServerType->addItem("客户端", 2);
        hBox->addWidget(fdServerType);

        hBox->addSpacing(10);

        lbTraficPort = new QLabel;
        hBox->addWidget(lbTraficPort);

        auto fdPort = new QSpinBox;
        fdPort->setRange(0, 99999);
        fdPort->setValue(5000);
        hBox->addWidget(fdPort);

        btnTraficSet = new QPushButton;
        btnTraficSet->setProperty("ssType", "progManageTool");
        connect(btnTraficSet, &QPushButton::clicked, this, [=] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QJsonObject json;
            json.insert("_id", "SetCardMode");
            json.insert("_type", "SetCardMode");
            json.insert("serverType", fdServerType->currentData().toInt());
            json.insert("port", fdPort->value());
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("Setting card work mode ..."));
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSetReqAfter
                });
            } else {
                foreach(auto card, gSelCards) {
                    Def_CtrlSetMulti(tr("Set card work mode"))
                }
            }
        });
        hBox->addWidget(btnTraficSet);

        btnTraficGet = new QPushButton;
        btnTraficGet->setProperty("ssType", "progManageTool");
        connect(btnTraficGet, &QPushButton::clicked, this, [=] {
            if(gSelCards.isEmpty()) {
                QMessageBox::information(this, tr("Tip"), tr("NoSelectedController"));
                return;
            }
            QJsonObject json;
            json.insert("_id", "GetCardMode");
            json.insert("_type", "GetCardMode");
            if(gSelCards.count() == 1) {
                auto waitingDlg = new WaitingDlg(this, tr("Getting card work mode ..."));
                Def_CtrlReqPre
                connect(reply, &QNetworkReply::finished, this, [=] {
                    Def_CtrlSingleGetReply
                    waitingDlg->success();
                    setCurrentData(fdServerType, json["serverType"].toInt());
                    fdPort->setValue(json["port"].toInt());
                });
            } else {
                foreach(auto card, gSelCards) {
                    auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
                    auto cardId = card.id;
                    connect(reply, &QNetworkReply::finished, this, [=] {
                        QJsonDocument json;
                        QString err = checkReplyForJson(reply, &json);
                        if(err.isEmpty()) {
                            err = tr("Card work mode")+": ";
                            auto serverType = json["serverType"].toInt();
                            int idx = fdServerType->findData(serverType);
                            if(idx!=-1) err += fdServerType->itemText(idx);
                            else err += QString::number(serverType);
                            err += ". "+tr("Port")+": "+QString::number(json["port"].toInt());
                        }
                        gFdResInfo->append(cardId+" "+tr("Get card work mode")+" "+err);
                    });
                }
            }
        });
        hBox->addWidget(btnTraficGet);
        hBox->addStretch();
    }
    vBox->addWidget(grpBoxHiddenSettings);
    vBox->addStretch();

    connect(gDevicePanel, &DevicePanel::sigSelectedDeviceList, this, [this] {
        if(isVisible()) init();
    });
    transUi();
}

void CtrlAdvancedPanel::showEvent(QShowEvent *event) {
    QWidget::showEvent(event);
    init();
}
void CtrlAdvancedPanel::init() {
    if(! isPassed) {
        hide();
        QDialog dlg(this);
#ifdef Q_OS_WIN
        dlg.setWindowFlag(Qt::WindowContextHelpButtonHint, false);
#endif
        dlg.resize(240, 150);

        auto vBox = new VBox(&dlg);
        vBox->addStretch();
        auto hBox = new HBox(vBox);
        hBox->addWidget(new QLabel(tr("Input password")));

        auto fdPassword = new QLineEdit;
        fdPassword->setEchoMode(QLineEdit::Password);
        fdPassword->setFocus();
        hBox->addWidget(fdPassword);

        auto btnChangePassword = new QPushButton(tr("Change Password"));
        vBox->addWidget(btnChangePassword, 0, Qt::AlignRight);
        connect(btnChangePassword, &QPushButton::clicked, &dlg, [&dlg] {
            ChangePasswordForm cdlg(&dlg);
            cdlg.exec();
        });
        vBox->addStretch();

        auto btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
        connect(btnBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
        vBox->addWidget(btnBox);

        connect(btnBox, &QDialogButtonBox::accepted, &dlg, [=, &dlg] {
            QString pwdRaw = QSettings().value("advUiPs").toString();
            QString password = pwdRaw.isEmpty() ? "888" : QString::fromUtf8(QByteArray::fromBase64(pwdRaw.toLatin1()));
            if(fdPassword->text() == password) dlg.accept();
            else QMessageBox::critical(&dlg, tr("Tip"),tr("Password is error"));
        });
        if(dlg.exec()==QDialog::Accepted) {
            isPassed = true;
            show();
        } else return;
    }

    bool isSingle = gSelCards.count()==1;
    btnApkCheck->setEnabled(isSingle);
    btnRestart->setEnabled(isSingle);
    btnGetLog->setEnabled(isSingle);

    if(! isSingle) {
        grpM80->setVisible(true);
        grpY50->setVisible(true);
        return;
    }
    auto card = gSelCards[0];
    fdScreenWidth->setText(QString::number(card.mWidth));
    fdScreenHeight->setText(QString::number(card.mHeight));
    fdAlias->setText(card.alias);
    
    auto isM80 = card.id.startsWith("M8", Qt::CaseInsensitive);
    grpM80->setVisible(isM80);
    if(isM80) {
        QJsonObject json;
        json.insert("_id", "GetAllScreenSizeM80");
        json.insert("_type", "GetAllScreenSizeM80");
        auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
        connect(reply, &QNetworkReply::finished, this, [this, reply] {
            QJsonDocument json;
            QString err = checkReplyForJson(reply, &json);
            if(! err.isEmpty()) return;
            auto sizes = json["result"].toObject();
            fdM80Resolu->clear();
            auto send = sizes.constEnd();
            for(auto size=sizes.constBegin(); size<send; size++) fdM80Resolu->addItem(size.key(), size.value().toString());
        });
    }

    auto isY50 = card.id.startsWith("st5", Qt::CaseInsensitive)
                 || card.id.startsWith("m5s", Qt::CaseInsensitive)
                 || card.id.startsWith("m6s", Qt::CaseInsensitive)
                 || card.id.startsWith("m7s", Qt::CaseInsensitive)
                 || card.id.startsWith("y5", Qt::CaseInsensitive);
    grpY50->setVisible(isY50);

    QJsonObject json;
    json.insert("_id", "GetOnlineAddr");
    json.insert("_type", "GetOnlineAddr");
    auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
    connect(reply, &QNetworkReply::finished, this, [this, reply] {
        QJsonDocument json;
        QString err = checkReplyForJson(reply, &json);
        if(! err.isEmpty()) return;
        fdWebServerAddr->setCurrentText(json["server"].toString());
        fdCompanyId->setText(json["companuID"].toString());
    });

    json = QJsonObject();
    json.insert("_id", "GetRealtimeServer");
    json.insert("_type", "GetRealtimeServer");
    reply = NetReq("http://"+card.ip+":2016/settings").timeout(120000).post(json);
    connect(reply, &QNetworkReply::finished, this, [this, reply] {
        QJsonDocument json;
        QString err = checkReplyForJson(reply, &json);
        if(! err.isEmpty()) return;
        fdRealtimeServer->setCurrentText(json["server"].toString());
    });
}
void CtrlAdvancedPanel::changeEvent(QEvent *event) {
    QWidget::changeEvent(event);
    if(event->type() == QEvent::LanguageChange) transUi();
}
void CtrlAdvancedPanel::transUi() {
    btnBindTaxiIc->setText(tr("Binding *.ic account indentity voucher"));
    btnGetTopLevel->setText(tr("Readback"));
    btnLedSet->setText(tr("Start LedSet4"));

    grpM80->setTitle("M80 "+tr("Config"));
    btnM80Refresh->setText(tr("Refresh"));
    btnM80Restore->setText(tr("Restore to default"));
    btnM80Set->setText(tr("Set"));

    grpY50->setTitle("M50S / M60S / M70S / M5H / ST50 / Y1G / Y1C / Y4A / Y5A "+tr("Resolution Config"));
    fdDisMode->setItemText(0, tr("Full screen"));
    fdDisMode->setItemText(1, tr("Part"));
    btnY50Set->setText(tr("Set"));

    lbDisMode->setText(tr("Display Mode"));
    btnDisModeSet->setText(tr("Set"));
    btnDisModeGet->setText(tr("Get"));

    lbScreenPos->setText(tr("Screen Position"));
    lbScreenOff->setText(tr("Offset"));
    btnScreenOffSet->setText(tr("Set"));
    btnScreenOffGet->setText(tr("Get"));

    btnAliasSet->setText(tr("Set"));
    btnScreenSet->setText(tr("Set"));
    btnHighForBusySet->setText(tr("Set"));
    fdScreenHeight->setPlaceholderText(tr("Height"));
    fdScreenWidth->setPlaceholderText(tr("Width"));
    fdHighForBusy->setText(tr("Service:High  Out of service:Low"));
    fdTopLevelLH->setText(tr("Service:Low  Out of service:High"));
    lbRotate->setText(tr("Rotate"));
    btnRotateSet->setText(tr("Set"));
    btnRotateGet->setText(tr("Get"));

    grpBoxHiddenSettings->setTitle(tr("Hidden Settings")+" ("+tr("Click right button to hide")+")");
    btnSysUpd->setText(tr("System Update"));
    btnMcuUpd->setText(tr("Update MCU"));
    btnMcuGet->setText(tr("Get MCU Version"));
    lbBaudCfg->setText(tr("Baud Config"));
    lbBaudModel->setText(tr("Model"));
    lbUart->setText(tr("Uart"));
    lbBaud->setText(tr("Baud"));
    btnBaudSet->setText(tr("Set"));
    btnBaudGet->setText(tr("Get"));
    fdIsOpenADB->setText(tr("Open ADB"));
    lbCustomJson->setText(tr("Post Custom JSON"));
    btnSendCustomJson->setText(tr("Send"));

    grpHighForBusy->setTitle(tr("Taxi top screen configuration"));
    label->setText(tr("Realtimer Server Address:"));
    lbTitle->setText(tr("Advanced"));
    lbCompanyId->setText(tr("Compant ID:"));
    labelWebServer->setText(tr("Web Server Address:"));
    lbMinBright->setText(tr("Min brightness"));
    label_3->setText(tr("Max brightness "));
    lbAlias->setText(tr("Alias"));
    lbScreenHeight->setText(tr("Height"));
    lbScreenWidth->setText(tr("Screen Width(pixel)"));
    fdCompanyId->setPlaceholderText(tr("Compant ID"));

    btnWareUpdate->setText(tr("Firmware Management"));
    lbWareTip->setText("(APK / FPGA "+tr("update or uninstall")+")");
    btnApkCheck->setText(tr("Check Apk"));
    btnGetLog->setText(tr("Check Log"));
    btnSetBack->setText(tr("Set Wallpaper"));
    btnClearProg->setText(tr("Clear Program"));
    btnGetPlayerState->setText(tr("Get Player State"));
    btnPlayerBackSet->setText(tr("Set player background"));
    btnPlayerBackClear->setText(tr("Clear player background"));

    btnRealtimeClear->setText(tr("Clear"));
    btnMinBrightSet->setText(tr("Set"));
    btnMinBrightGet->setText(tr("Readback"));
    btnMaxBrightSet->setText(tr("Set"));
    btnMaxBrightGet->setText(tr("Readback"));
    btnRestart->setText(tr("Restart"));
    btnIsRunning->setText(tr("Running check"));
    btnRealtimeServerSet->setText(tr("Set"));
    btnWebServerSet->setText(tr("Set"));
    fdUninstall->setText(tr("Uninstall"));

    lbTimingReboot->setText(tr("Timing Reboot"));
    btnTimingRebootSet->setText(tr("Set"));
    btnTimingRebootGet->setText(tr("Get"));

    lbTraficProtocol->setText(tr("Protocol"));
    lbCardMode->setText(tr("Card work mode"));
    fdServerType->setItemText(0, tr("Server"));
    fdServerType->setItemText(1, tr("Client"));
    lbTraficPort->setText(tr("Port"));
    btnTraficProtSet->setText(tr("Set"));
    btnTraficProtGet->setText(tr("Get"));
    btnTraficSet->setText(tr("Set"));
    btnTraficGet->setText(tr("Get"));
}

void messageHandler(QtMsgType type, const QMessageLogContext &, const QString &msg) {
    auto cur = QDateTime::currentDateTime();
    QFile file("D:/LedOK-advanced-"+cur.toString("yy-MM-dd")+".log");
    file.open(QIODevice::WriteOnly | QIODevice::Append);
    file.write(cur.toString("yy-MM-dd hh:mm:ss ").toUtf8());
    if(type==QtDebugMsg)        file.write("   Debug: ");
    else if(type==QtWarningMsg) file.write(" Warning: ");
    else if(type==QtCriticalMsg)file.write("Critical: ");
    else if(type==QtFatalMsg)   file.write("   Fatal: ");
    else if(type==QtInfoMsg)    file.write("    Info: ");
    else if(type==QtSystemMsg)  file.write("  System: ");
    file.write(msg.toUtf8());
    file.close();
}
void CtrlAdvancedPanel::keyReleaseEvent(QKeyEvent *event) {
    auto key = event->key();
    if(key == Qt::Key_F5) grpBoxHiddenSettings->setVisible(! grpBoxHiddenSettings->isVisible());
    else if(key == Qt::Key_F6) grpMinMaxBrightness->setVisible(! grpMinMaxBrightness->isVisible());
    else if(key == Qt::Key_F8) grpHighForBusy->setVisible(! grpHighForBusy->isVisible());
    else if(key == Qt::Key_F12) qInstallMessageHandler(messageHandler);
    QWidget::keyPressEvent(event);
}
void CtrlAdvancedPanel::mouseReleaseEvent(QMouseEvent *event){
    if(event->button()==Qt::RightButton) grpBoxHiddenSettings->setVisible(! grpBoxHiddenSettings->isVisible());
}


PlayerBackSendThread::PlayerBackSendThread(const QString &file, const QString &ip) : file(file), ip(ip) {
    connect(this, &QThread::finished, this, &QThread::deleteLater);
}
void PlayerBackSendThread::run() {
    TcpSocket tcp;
    tcp.connectToHost(ip, 3333);
    if(! tcp.waitForConnected()) {
        emit emErr(QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") when waitForConnected");
        tcp.close();
        return;
    }
    QFileInfo info(file);
    auto baseName = info.baseName();
    auto remain = info.size();

    auto req = QJsonObject();
    req.insert("_type", "proStart");
    req.insert("proName", "program");
    req.insert("proSize", remain);
    req.insert("zVer","xixun1");
    auto resNum = tcp.write(QJsonDocument(req).toJson(QJsonDocument::Compact));
    if(resNum == -1 || ! tcp.waitForBytesWritten()) {
        emit emErr(QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") "+tcp.errorString()+" when write 'proStart'");
        tcp.close();
        return;
    }
    req = QJsonObject();
    req.insert("_type", "imgFileStart");
    req.insert("id", baseName);
    req.insert("size", remain);
    req.insert("zVer","xixun1");
    resNum = tcp.write(QJsonDocument(req).toJson(QJsonDocument::Compact));
    if(resNum == -1 || ! tcp.waitForBytesWritten()) {
        emit emErr(QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") "+tcp.errorString()+" when write 'imgFileStart'");
        tcp.close();
        return;
    }
    auto file = new QFile(info.filePath());
    if(! file->open(QFile::ReadOnly)) {
        emit emErr(tr("Open file failed")+" "+file->errorString());
        tcp.close();
        return;
    }
    while(remain > 0) {
        auto readed = file->read(qMin(4096LL, remain));
        if(readed.isEmpty()) {
            emit emErr(tr("Read file failed")+" "+file->errorString());
            tcp.close();
            file->close();
            return;
        }
        resNum = tcp.write(readed);
        if(resNum == -1) {
            emit emErr(QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") "+tcp.errorString()+" when write file: "+file->fileName());
            tcp.close();
            file->close();
            return;
        }
        if(! tcp.waitForBytesWritten()) {
            emit emErr(QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") "+tcp.errorString()+" when waitForWritten file: "+file->fileName());
            tcp.close();
            file->close();
            return;
        }
        remain -= resNum;
    }
    file->close();
    req = QJsonObject();
    req.insert("_type", "imgFileEnd");
    req.insert("id", baseName);
    req.insert("zVer","xixun1");
    resNum = tcp.write(QJsonDocument(req).toJson(QJsonDocument::Compact));
    if(resNum == -1 || ! tcp.waitForBytesWritten()) {
        emit emErr(QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") "+tcp.errorString()+" when write 'fileEnd'");
        tcp.close();
        return;
    }
    req = QJsonObject();
    req.insert("_type", "proEnd");
    req.insert("proName", "program");
    req.insert("zVer","xixun1");
    resNum = tcp.write(QJsonDocument(req).toJson(QJsonDocument::Compact));
    if(resNum == -1 || ! tcp.waitForBytesWritten()) {
        emit emErr(QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") "+tcp.errorString()+" when write 'proEnd'");
        tcp.close();
        return;
    };
    if(! tcp.waitForReadyRead()) {
        emit emErr(QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") "+tcp.errorString()+" when waitForRead 'proEnd'");
        tcp.close();
        return;
    }
    auto resp = tcp.readAll();
    if(resp.isEmpty()) {
        emit emErr(QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") "+tcp.errorString()+" when read 'proEnd'");
        tcp.close();
        return;
    }
    tcp.close();
    emit emErr("");
}