#include "mainwindow.h" #include "main.h" #include "gutil/qnetwork.h" #include "gutil/qwaitingdlg.h" #include #include #include #include #include #include MainWindow::MainWindow() { resize(1180, 680); auto vBox = new VBox(this); vBox->setContentsMargins(0,0,0,0); QIcon green(":/res/green.png"); QIcon yellow(":/res/yellow.png"); QIcon plus(":/res/plus.png"); QIcon red(":/res/red.png"); iconss = {{green, green}, {yellow, yellow, yellow}, {yellow, yellow, plus}, {red, red, red}}; auto hBox = new HBox(vBox); hBox->setSpacing(0); auto vv = new VBox(hBox); addPart(vv); auto line = new QFrame; line->setFrameStyle(QFrame::Sunken); line->setFrameShape(QFrame::VLine); hBox->addWidget(line); vv = new VBox(hBox); addPart(vv); hBox = new HBox(vBox); hBox->addSpacing(80); hBox->addStretch(); auto lb = hBox->addLabel("Daylite® Terminal Control"); auto ft = lb->font(); ft.setPixelSize(28); lb->setFont(ft); hBox->addStretch(); btnChg = new QPushButton("Change Password"); btnChg->hide(); connect(btnChg, &QPushButton::clicked, this, [=] { bool ok = false; auto pwd = QInputDialog::getText(this, "Input New Password", "New Password", QLineEdit::Password, "", &ok); if(! ok) return; if(pwd.isEmpty()) { QMessageBox::warning(this, "Error", "Password is Empty"); return; } auto pwdaga = QInputDialog::getText(this, "Confirm Your New Password", "Confirm Password", QLineEdit::Password, "", &ok); if(! ok) return; if(pwd.isEmpty()) { QMessageBox::warning(this, "Error", "Password is Empty"); return; } if(pwd!=pwdaga) { QMessageBox::warning(this, "Error", "Two Inputs are Different"); return; } QCryptographicHash cryptoHash(QCryptographicHash::Md5); cryptoHash.addData(pwd.toUtf8()); auto md5 = QString::fromLatin1(cryptoHash.result().toHex()); cfg["pwd"] = md5; QFile file("cfg.json"); if(! file.open(QFile::WriteOnly)) return; file.write(JToBytes(cfg, "\t")); file.close(); }); hBox->addWidget(btnChg); btnCcl = new QPushButton("Cancel"); btnCcl->setMinimumWidth(100); btnCcl->hide(); connect(btnCcl, &QPushButton::clicked, this, [=] { btnChg->hide(); btnCcl->hide(); btnOK->hide(); btnEdit->show(); for(auto &card : cards) { card.edId->hide(); card.edName->hide(); card.lbName->show(); for(int i=0; ihide(); card.btns[i]->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); } } }); hBox->addWidget(btnCcl); btnOK = new QPushButton("OK"); btnOK->setMinimumWidth(100); btnOK->hide(); connect(btnOK, &QPushButton::clicked, this, [=] { btnChg->hide(); btnCcl->hide(); btnOK->hide(); btnEdit->show(); JArray jCards; for(auto &card : cards) { auto name = card.edName->text(); card.lbName->setText(name); card.id = card.edId->currentText(); card.lbAlias->setText(card.id); auto idx = card.edId->findText(card.id); if(idx>-1) { card.ip = card.edId->itemData(idx).toString(); card.online = card.edId->itemData(idx, Qt::UserRole+1).toBool(); card.lbStatus->setText("Online"); auto pal = card.lbStatus->palette(); pal.setColor(QPalette::WindowText, 0x00aa00); card.lbStatus->setPalette(pal); getNames(card); } else { card.ip = ""; card.online = false; card.lbStatus->setText("Offline"); auto pal = card.lbStatus->palette(); pal.setColor(QPalette::WindowText, 0x777777); card.lbStatus->setPalette(pal); card.lbCur->setText(""); } card.edId->hide(); card.edName->hide(); card.lbName->show(); JArray cmds; for(int i=0; isetText(card.edits[i]->text()); cmds.append(card.edits[i]->text()); card.edits[i]->hide(); card.btns[i]->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); } jCards.append(JObj{{"name", name}, {"id", card.id}, {"cmds", cmds}}); } cfg["cards"] = jCards; QFile file("cfg.json"); if(! file.open(QFile::WriteOnly)) return; file.write(JToBytes(cfg, "\t")); file.close(); }); hBox->addWidget(btnOK); btnEdit = new QPushButton(QIcon(":/res/settings.png"), "Edit"); btnEdit->setIconSize({32,32}); connect(btnEdit, &QPushButton::clicked, this, [=] { auto pwdmd5 = cfg["pwd"].toStr(); if(! pwdmd5.isEmpty()) { bool ok = false; auto pwd = QInputDialog::getText(this, "Input Your Password", "Password", QLineEdit::Password, "", &ok); if(! ok) return; if(pwd.isEmpty()) { QMessageBox::warning(this, "Error", "Password is Empty"); return; } QCryptographicHash cryptoHash(QCryptographicHash::Md5); cryptoHash.addData(pwd.toUtf8()); auto md5 = QString::fromLatin1(cryptoHash.result().toHex()); if(md5!=pwdmd5) { QMessageBox::warning(this, "Error", "Password Error"); return; } } btnEdit->hide(); btnChg->show(); btnCcl->show(); btnOK->show(); for(auto &card : cards) { card.edName->setText(card.lbName->text()); card.edId->setCurrentText(card.id); card.lbName->hide(); card.edName->show(); card.edId->show(); for(int i=0; isetText(card.btns[i]->text()); card.btns[i]->setToolButtonStyle(Qt::ToolButtonIconOnly); card.edits[i]->show(); } } }); hBox->addWidget(btnEdit); // fdPort->setSizeAdjustPolicy(QComboBox::AdjustToContents); show(); readfile(); JArray jCards; if(cfg.empty() || (jCards = cfg["cards"].toArray()).empty()) { cfg["cards"] = jCards = JArray{ JObj{{"name", "Card 1"}, {"cmds", JArray{"AAA","AAA", "BBB","BBB","BBB", "BBB","BBB","CCC", "DDD","DDD","DDD"}}}, JObj{{"name", "Card 2"}, {"cmds", JArray{"AAA","AAA", "BBB","BBB","BBB", "BBB","BBB","CCC", "DDD","DDD","DDD"}}} }; } for(int cc=0; ccsetText(jCards[cc]["name"].toStr()); cards[cc].lbAlias->setText(cards[cc].id); auto cmds = jCards[cc]["cmds"]; for(int bb=0; bbsetText(cmds[bb].toStr()); } connect(&mUdpSocket, &QUdpSocket::readyRead, this, [this] { while(mUdpSocket.hasPendingDatagrams()) { auto gram = mUdpSocket.receiveDatagram(); auto data = gram.data(); if(data.isEmpty()) continue; auto senderAddress = gram.senderAddress(); bool ok = true; if(senderAddress.protocol()==QUdpSocket::IPv6Protocol) senderAddress.setAddress(senderAddress.toIPv4Address(&ok)); auto addr = ok ? senderAddress.toString() : ""; QString error; auto json = JFrom(gram.data(), &error); if(! error.isEmpty()) { qDebug()<<"UDP read Json Error: "+error; continue; } auto cardId = json["cardId"].toString(); for(auto &card : cards) if(card.id==cardId) { card.ip = addr; card.online = true; card.lbStatus->setText("Online"); auto pal = card.lbStatus->palette(); pal.setColor(QPalette::WindowText, 0x00aa00); card.lbStatus->setPalette(pal); getNames(card); } int cnt = cards[0].edId->count(); for(int i=0; iitemText(i)==cardId) { for(auto &card : cards) { card.edId->setItemData(i, addr); card.edId->setItemData(i, true, Qt::UserRole+1); } goto end; } for(auto &card : cards) { auto iii = card.edId->currentIndex(); card.edId->addItem(cardId, addr); if(iii==-1) card.edId->setCurrentIndex(-1); card.edId->setItemData(cnt, true, Qt::UserRole+1); } end:; } }); sendGetInfo(); timerId = startTimer(10000); } void MainWindow::readfile() { QFile file("cfg.json"); if(! file.open(QFile::ReadOnly)) return; QString err; auto json = JFrom(&file, &err); if(! err.isEmpty()) return; cfg = json.toObj(); } void MainWindow::addPart(VBox *vv) { vv->addStretch(); auto lbName = new QLabel; auto ft = lbName->font(); ft.setPixelSize(22); lbName->setFont(ft); vv->addWidget(lbName, 0, Qt::AlignCenter); auto edName = new QLineEdit; edName->hide(); vv->addWidget(edName); auto edId = new QComboBox; edId->setEditable(true); edId->hide(); vv->addWidget(edId); vv->addStretch(); auto hh = new HBox(vv); auto lb = hh->addLabel("Status: "); lb->setMinimumWidth(120); lb->setAlignment(Qt::AlignVCenter | Qt::AlignRight); auto lbStatus = hh->addLabel("Offline"); auto pal = lbStatus->palette(); pal.setColor(QPalette::WindowText, 0x777777); lbStatus->setPalette(pal); hh->addStretch(); vv->addStretch(); hh = new HBox(vv); lb = hh->addLabel("Aktuell: "); lb->setMinimumWidth(120); lb->setAlignment(Qt::AlignVCenter | Qt::AlignRight); auto lbAlias = hh->addLabel(); hh->addLabel(" / "); auto lbCur = hh->addLabel(); hh->addStretch(); cards.push_back({lbName, edName, edId, lbStatus, lbAlias, lbCur, new QButtonGroup(hh)}); vv->addStretch(); QSize siz{100, 100}; auto &card = cards.back(); auto cccc = cards.size()-1; for(auto &icons : iconss) { auto hh = new HBox(vv); vv->addStretch(); hh->addStretch(1); for(auto &icon : icons) { auto vvv = new VBox; hh->addLayout(vvv, 4); vvv->setSpacing(0); auto btn = new QToolButton; btn->setStyleSheet("QToolButton{border: none;} QToolButton:checked,QToolButton:hover {border: 1px solid #aaa;}"); btn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); btn->setIcon(icon); btn->setIconSize(siz); vvv->addWidget(btn, 0, Qt::AlignCenter); card.btnGrp->addButton(btn, (int)card.btns.size()); card.btns.emplace_back(btn); auto ed = new QLineEdit; ed->setAlignment(Qt::AlignCenter); ed->hide(); vvv->addWidget(ed, 1); card.edits.emplace_back(ed); } hh->addStretch(1); } connect(card.btnGrp, &QButtonGroup::idClicked, this, [=](int id) { auto &card = cards[cccc]; if(card.ip.isEmpty()) QMessageBox::critical(this, "Error", "No IP"); else { auto name = card.btnGrp->button(id)->text(); if(QMessageBox::question(this, "Confirm", "Send "+name+"?")==QMessageBox::Yes && SendAnycastCmd(id + 1, card)) card.lbCur->setText(name); } }); } void MainWindow::sendGetInfo() { auto data = JToBytes(JObj{{"action", "getInfo"}}); uchar ccc[]{0x7E, 0x7E, 0x7E, 0x90, 0x42, 0x72, 0x6F, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x9F}; if(mUdpSocket.writeDatagram(data, QHostAddress("255.255.255.255"), 22222) != data.length()) qDebug() << "getInfo write to 255.255.255.255 failed"; if(mUdpSocket.writeDatagram((char *)ccc, sizeof(ccc), QHostAddress("255.255.255.255"), 31296) != sizeof(ccc)) qDebug() << "getInfo write to 255.255.255.255 failed"; auto networkinterfaces = QNetworkInterface::allInterfaces(); for(auto &face : networkinterfaces) { auto flags = face.flags(); bool can = (flags & QNetworkInterface::IsRunning) && (flags & QNetworkInterface::CanBroadcast) && ! (flags & QNetworkInterface::IsLoopBack); if(! can) continue; auto addrEntries = face.addressEntries(); for(QNetworkAddressEntry &addrEntry : addrEntries) { auto ip = addrEntry.ip(); if(ip.protocol()!=QAbstractSocket::IPv4Protocol) continue; auto broadcast = addrEntry.broadcast(); if(mUdpSocket.writeDatagram(data, broadcast, 22222) != data.length()) qDebug() << "getInfo write failed." << ip.toString() << "->" << broadcast.toString(); } } } unsigned char GetCheckCodeIn8(unsigned char *str, unsigned int size) { unsigned char checkCode = 0; for(int i=0; i<(int)size; i++) checkCode += str[i]; return (~checkCode) & 0xff; } struct ST_ANSY_PROGRAM_PACKET { unsigned char SyncHead[3]{0x7e, 0x7e, 0x55}; unsigned char ucCommType; int iBaoLiu; unsigned int iLength; unsigned char pDataBuffer[20]; }; class LocalObj : public QObject { public : int cnt = 1; void reset() { cnt = 0; } }; bool MainWindow::SendAnycastCmd(int progIdx, const Card &card) { ST_ANSY_PROGRAM_PACKET tempStreadPakcet; tempStreadPakcet.ucCommType = 0x97; tempStreadPakcet.iBaoLiu = 0; tempStreadPakcet.iLength = 4; unsigned char uctemp[4] = {0}; uctemp[0] = progIdx; memcpy(tempStreadPakcet.pDataBuffer, uctemp, 4); tempStreadPakcet.pDataBuffer[tempStreadPakcet.iLength] = GetCheckCodeIn8(&tempStreadPakcet.ucCommType,tempStreadPakcet.iLength+sizeof(tempStreadPakcet.iBaoLiu)+sizeof(tempStreadPakcet.ucCommType)+sizeof(tempStreadPakcet.iLength)); int iLenPacket = 3*sizeof(unsigned char) + sizeof(char) + sizeof(int) + sizeof(int) + tempStreadPakcet.iLength + sizeof(char); //除正文外的协议结构大小; auto data = QByteArray(reinterpret_cast(&tempStreadPakcet), iLenPacket); auto action = progIdx==0 ? tr("Reset loop mode") : tr("Anycast"); auto waitingDlg = new WaitingDlg(this, action+" ..."); LocalObj lll; connect(waitingDlg, &QTextEdit::destroyed, &lll, &LocalObj::reset); waitingDlg->setAttribute(Qt::WA_DeleteOnClose); waitingDlg->show(); TcpSocket tcp; tcp.connectToHost(card.ip, 31299); if(! tcp.waitForConnected(10000)) { tcp.close(); if(lll.cnt) { waitingDlg->close(); QMessageBox::critical(this, tr("Tip"), QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") "+tcp.errorString()+" at WaitConnected\n"); } return false; } auto resNum = tcp.write(data); tcp.flush(); if(resNum == -1 || ! tcp.waitForBytesWritten(10000)) { tcp.close(); if(lll.cnt) { waitingDlg->close(); QMessageBox::critical(this, tr("Tip"), QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") "+tcp.errorString()+" at write\n"); } return false; } if(! tcp.waitForReadyRead(10000)) { tcp.close(); if(lll.cnt) { waitingDlg->close(); QMessageBox::critical(this, tr("Tip"), QString(socketErrKey(tcp.error()))+" ("+QString::number(tcp.error())+") "+tcp.errorString()+" at WaitRead\n"); } return false; } tcp.close(); if(lll.cnt) waitingDlg->success(); return true; } MainWindow::~MainWindow() { mUdpSocket.close(); if(timerId) killTimer(timerId); } void MainWindow::getNames(const Card &card) { JObj json; json.insert("_id", "GetProgramName"); json.insert("_type", "GetProgramName"); auto reply = NetReq("http://"+card.ip+":2016/settings").timeout(10000).post(json); connect(reply, &QNetworkReply::finished, this, [=] { JValue json; auto err = errStrWithJson(reply, &json); if(! err.isEmpty()) return; card.lbCur->setText(json["programName"].toStr()); }); json = JObj(); json.insert("_id", "GetCardAlias"); json.insert("_type", "GetCardAlias"); reply = NetReq("http://"+card.ip+":2016/settings").timeout(10000).post(json); connect(reply, &QNetworkReply::finished, this, [=] { JValue json; auto err = errStrWithJson(reply, &json); if(! err.isEmpty()) return; auto alias = json["alias"].toStr(); if(! alias.isEmpty()) card.lbAlias->setText(QString::fromUtf8(QByteArray::fromBase64(alias.toLatin1()))); }); } void MainWindow::timerEvent(QTimerEvent *event) { if(timerId!=event->timerId()) return; for(auto &card : cards) if(card.online) card.online = false; else { card.lbStatus->setText("Offline"); auto pal = card.lbStatus->palette(); pal.setColor(QPalette::WindowText, 0x777777); card.lbStatus->setPalette(pal); } int cnt = cards[0].edId->count(); for(int i=0; iitemData(i, Qt::UserRole+1).toBool()) for(auto &card : cards) card.edId->setItemData(i, false, Qt::UserRole+1); else { for(auto &card : cards) card.edId->removeItem(i); i--; cnt--; } sendGetInfo(); }