qt/daylite/mainwindow.cpp

481 lines
18 KiB
C++
Raw Normal View History

2025-05-06 18:28:05 +08:00
#include "mainwindow.h"
#include "main.h"
#include "gutil/qnetwork.h"
#include "gutil/qwaitingdlg.h"
#include <QPushButton>
#include <QMessageBox>
#include <QInputDialog>
#include <QFile>
#include <QNetworkInterface>
#include <QNetworkDatagram>
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; i<card.btns.size(); i++) {
card.edits[i]->hide();
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; i<card.btns.size(); i++) {
card.btns[i]->setText(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; i<card.btns.size(); i++) {
card.edits[i]->setText(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; cc<cards.size() && cc<jCards.size(); cc++) {
cards[cc].id = jCards[cc]["id"].toStr();
cards[cc].lbName->setText(jCards[cc]["name"].toStr());
cards[cc].lbAlias->setText(cards[cc].id);
auto cmds = jCards[cc]["cmds"];
for(int bb=0; bb<cards[cc].btns.size() && bb<cmds.size(); bb++) cards[cc].btns[bb]->setText(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; i<cnt; i++) if(cards[0].edId->itemText(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<char*>(&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; i<cnt; i++) if(cards[0].edId->itemData(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();
}