1 /*
2 * Copyright (c) 2023 Unionman Technology Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "StbItemWidget.h"
17 #include "MainWindow.h"
18 #include "ui_stb_item_widget.h"
19 #include <QDateTime>
20 #include <QDesktopServices>
21 #include <QDir>
22 #include <QInputDialog>
23 #include <QLineEdit>
24 #include <QMenu>
25 #include <QMessageBox>
26 #include <QMouseEvent>
27 #include <QProcess>
28 #include <QUrl>
29
StbItemWidget(QWidget * parent)30 StbItemWidget::StbItemWidget(QWidget* parent)
31 :QWidget(parent), ui(new Ui::StbItemWidget)
32 {
33 ui->setupUi(this);
34 setLayout(ui->horizontalLayout);
35 ui->snLabel->setText("上线");
36 ui->uptimeLabel->setText("");
37 this->parent = parent;
38 setContextMenuPolicy(Qt::CustomContextMenu);
39 connect(this, &StbItemWidget::customContextMenuRequested, this, &StbItemWidget::openMenu);
40 }
41
~StbItemWidget()42 StbItemWidget::~StbItemWidget()
43 {
44 delete ui;
45 }
46
openConfigWeb()47 void StbItemWidget::openConfigWeb()
48 {
49 if (ip.contains(":")) {
50 QDesktopServices::openUrl(QUrl("http://[" + ip + "]"));
51 } else {
52 QDesktopServices::openUrl(QUrl("http://" + ip));
53 }
54 }
55
playVideo()56 void StbItemWidget::playVideo()
57 {
58 qDebug() << "Ready to play video.";
59 QString urlPath = "/livestream/UM-ONVIF";
60 QString cmd = "start \"" + ip + "\"" + " .\\ffplay.exe rtsp://" + ip + urlPath
61 + " -x 1280 -y 720 -fflags nobuffer -rtsp_transport tcp";
62 system(cmd.toLatin1().data());
63 }
64
openADBShell()65 void StbItemWidget::openADBShell()
66 {
67 QString cmd
68 = "start \"" + ip + "\" cmd /K \"hdc -t " + ip + " shell\"";
69 system(cmd.toLatin1().data());
70 }
71
checkLog()72 void StbItemWidget::checkLog()
73 {
74 LogTextWidget* logTextWidget = new LogTextWidget(nullptr, qobject_cast<QWidget*>(this));
75 QString logTextWidgetTitle = this->sn == NULL ? "log (" + this->mac + ")" : "log (" + this->sn + ")";
76 logTextWidget->setWindowTitle(logTextWidgetTitle);
77 logTextWidget->show();
78
79 QString logFilePath = this->getLogPath();
80
81 this->logMutex.lock();
82 QFile file(logFilePath);
83 if (file.exists()) {
84 if (file.open(QIODevice::ReadOnly)) {
85 QString fileContent = QString(file.readAll());
86 logTextWidget->setLogText(fileContent);
87 file.close();
88 }
89 }
90 this->logMutex.unlock();
91 }
92
openLogDirectory()93 void StbItemWidget::openLogDirectory()
94 {
95 QString localApplicationDirPath = QCoreApplication::applicationDirPath();
96
97 QString logDir = localApplicationDirPath + "/log/" + (this->sn == NULL ? this->mac : this->sn);
98 QDir dir(logDir);
99 logDir.replace("/", QDir::separator());
100 if (!dir.exists()) {
101 if (!dir.mkpath(logDir)) {
102 QMessageBox errorBox;
103
104 errorBox.setWindowTitle("错误");
105 errorBox.setText("日志目录不存在,且尝试创建目录失败! ");
106
107 QFont font;
108 font.setPointSize(10L);
109 font.setWeight(QFont::Normal);
110 errorBox.setFont(font);
111
112 errorBox.setIcon(QMessageBox::Critical);
113 errorBox.exec();
114 return;
115 }
116 }
117
118 QUrl directoryUrl = QUrl::fromLocalFile(logDir);
119 QDesktopServices::openUrl(directoryUrl);
120 }
121
httpRequest(QString req)122 int StbItemWidget::httpRequest(QString req)
123 {
124 QString url = "";
125 if (QHostAddress(ip).protocol() == QAbstractSocket::IPv4Protocol) {
126 url = "http://" + ip + ":6060/" + req;
127 } else if (QHostAddress(ip).protocol() == QAbstractSocket::IPv6Protocol) {
128 url = "http://[" + ip + "]:6060/" + req;
129 }
130
131 QNetworkRequest request;
132 QNetworkAccessManager* naManager = new QNetworkAccessManager(this);
133 QMetaObject::Connection connRet
134 = QObject::connect(naManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
135 Q_ASSERT(connRet);
136
137 qDebug() << "url: " + url;
138 request.setUrl(QUrl(url));
139
140 QString testData = "";
141 QNetworkReply* reply = naManager->post(request, testData.toUtf8());
142 return 0;
143 }
144
burnSN()145 void StbItemWidget::burnSN()
146 {
147 qDebug() << "Ready to burn sn.";
148 bool getInfo;
149 QInputDialog* inputDialog = new QInputDialog();
150 QString title = "修改序列号";
151 QString config = inputDialog->getText(this->parent, title, "输入序列号", QLineEdit::Normal, sn, &getInfo);
152 qDebug() << "Config: " << config << ", getInfo: " << getInfo;
153 if (getInfo) {
154 if (config.isEmpty()) {
155 config = "sn=null";
156 } else if (!config.contains("=")) {
157 config = "sn=" + config;
158 }
159
160 httpRequest("SetDevInfo?" + config);
161 }
162 }
163
reboot()164 void StbItemWidget::reboot()
165 {
166 QMessageBox msgBox;
167 msgBox.setWindowTitle("提示");
168 msgBox.setText("确认重启设备?");
169
170 QFont font;
171 font.setPointSize(10L);
172 font.setWeight(QFont::Normal);
173 msgBox.setFont(font);
174
175 msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
176 msgBox.setDefaultButton(QMessageBox::No);
177
178 msgBox.setIcon(QMessageBox::Question);
179
180 if (msgBox.exec() == QMessageBox::Yes) {
181 QString logInfo = "设备通过升级工具触发重启!";
182 this->writeLog(logInfo);
183 qDebug() << "Ready reboot.";
184 httpRequest("SetDevInfo?shell=reboot");
185 }
186 }
187
enterFactoryMode()188 void StbItemWidget::enterFactoryMode()
189 {
190 QMessageBox msgBox;
191 msgBox.setWindowTitle("提示");
192 msgBox.setText("确认进入工厂模式?");
193
194 QFont font;
195 font.setPointSize(10L);
196 font.setWeight(QFont::Normal);
197 msgBox.setFont(font);
198
199 msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
200 msgBox.setDefaultButton(QMessageBox::No);
201
202 msgBox.setIcon(QMessageBox::Question);
203
204 if (msgBox.exec() == QMessageBox::Yes) {
205 QString url = "";
206 qDebug() << "Ready to enter factory.";
207 httpRequest("EnterFactoryMode");
208 }
209 }
210
restoreFactorySetting()211 void StbItemWidget::restoreFactorySetting()
212 {
213 QMessageBox msgBox;
214 msgBox.setWindowTitle("提示");
215 msgBox.setText("确认恢复出厂设置?");
216
217 QFont font;
218 font.setPointSize(10L);
219 font.setWeight(QFont::Normal);
220 msgBox.setFont(font);
221
222 msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
223 msgBox.setDefaultButton(QMessageBox::No);
224
225 msgBox.setIcon(QMessageBox::Question);
226
227 if (msgBox.exec() == QMessageBox::Yes) {
228 QString logInfo = "设备通过升级工具触发恢复出厂设置!";
229 this->writeLog(logInfo);
230 qDebug() << "Ready to restore factory setting.";
231 httpRequest("RestoreFactorySetting");
232 }
233 }
234
setIp(QString ip)235 void FilePusher::setIp(QString ip)
236 {
237 this->ip = ip;
238 QString title = "文件推送界面( " + this->ip + " )";
239 setWindowTitle(title);
240 }
241
selectFile()242 void FilePusher::selectFile()
243 {
244 QString filePath = QFileDialog::getOpenFileName(this, "选择文件", "", "All Files (*)");
245 if (!filePath.isEmpty()) {
246 filePathLineEdit->setText(filePath);
247 }
248 }
249
pushFile()250 void FilePusher::pushFile()
251 {
252 if (pushButton->text() == "推送") {
253 if (!filePathLineEdit->text().isEmpty() && !targetPathLineEdit->text().isEmpty()) {
254 m_filePusherThread.SetFilePath(filePathLineEdit->text());
255 m_filePusherThread.SetTargetPath(targetPathLineEdit->text());
256 m_filePusherThread.SetIp(ip);
257 filePathLineEdit->setEnabled(false);
258 targetPathLineEdit->setEnabled(false);
259 selectButton->setEnabled(false);
260
261 pushButton->setText("取消");
262
263 m_filePusherThread.start();
264 } else {
265 QMessageBox errBox;
266 QFont font;
267 font.setPointSize(10L);
268 font.setWeight(QFont::Normal);
269 errBox.setFont(font);
270 errBox.setWindowTitle("错误");
271 errBox.setText("文件路径和设备目标目录均不能为空! ");
272 errBox.setIcon(QMessageBox::Critical);
273 errBox.exec();
274 }
275 } else {
276 m_filePusherThread.CancelPush();
277
278 filePathLineEdit->setEnabled(true);
279 targetPathLineEdit->setEnabled(true);
280 selectButton->setEnabled(true);
281
282 pushButton->setText("推送");
283 }
284 }
285
handlePushMessage(const QString & result)286 void FilePusher::handlePushMessage(const QString& result)
287 {
288 resultTextEdit->setPlainText(result);
289 }
290
handlePushFinished(const QString & result)291 void FilePusher::handlePushFinished(const QString& result)
292 {
293 resultTextEdit->setPlainText(result);
294
295 filePathLineEdit->setEnabled(true);
296 targetPathLineEdit->setEnabled(true);
297 selectButton->setEnabled(true);
298
299 pushButton->setText("推送");
300 }
301
CancelPush()302 void FilePusherThread::CancelPush()
303 {
304 if (pushProcess.state() == QProcess::Running) {
305 pushProcess.kill();
306 }
307 pushProcessCancal = true;
308 }
309
Run()310 void FilePusherThread::Run()
311 {
312 QString adbPath = ".//adb//adb";
313 QStringList arguments;
314 arguments << "-s" << this->m_ip << "push" << this->m_filePath << this->m_targetPath;
315 QStringList connectArguments;
316 connectArguments << "connect" << this->m_ip;
317
318 emit PushMessage("文件推送中...");
319
320 pushProcess.setProgram(adbPath);
321 pushProcess.setArguments(connectArguments);
322 pushProcess.start();
323 pushProcess.waitForFinished();
324
325 if (pushProcessCancal) {
326 emit PushMessage("文件推送已取消!");
327 pushProcessCancal = false;
328 return;
329 }
330
331 pushProcess.setArguments(arguments);
332 pushProcess.start();
333 pushProcess.waitForFinished();
334
335 if (pushProcessCancal) {
336 emit PushMessage("文件推送已取消!");
337 pushProcessCancal = false;
338 return;
339 }
340
341 QByteArray output = pushProcess.readAllStandardOutput();
342 QString result = QString::fromLocal8Bit(output);
343 emit PushFinished(result);
344 }
345
pushFile()346 void StbItemWidget::pushFile()
347 {
348 this->filePusher.setIp(this->ip);
349 filePusher.show();
350 }
351
openMenu(const QPoint & pos)352 void StbItemWidget::openMenu(const QPoint& pos)
353 {
354 QMenu menu;
355 if (status != DEV_STATUS_OFFLINE) {
356 if (!sn.isEmpty()) {
357 connect(menu.addAction("Open Config Web"), &QAction::triggered, this, &StbItemWidget::openConfigWeb);
358 }
359
360 connect(menu.addAction("Play Live Video"), &QAction::triggered, this, &StbItemWidget::playVideo);
361 connect(menu.addAction("Burn SN"), &QAction::triggered, this, &StbItemWidget::burnSN);
362 connect(menu.addAction("Open HDC Shell"), &QAction::triggered, this, &StbItemWidget::openADBShell);
363 connect(menu.addAction("Restore Factory Setting"), &QAction::triggered, this,
364 &StbItemWidget::restoreFactorySetting);
365 connect(menu.addAction("Push File"), &QAction::triggered, this, &StbItemWidget::pushFile);
366
367 if (!sn.isEmpty()) {
368 connect(menu.addAction("Enter Factory Mode"), &QAction::triggered, this, &StbItemWidget::enterFactoryMode);
369 }
370
371 connect(menu.addAction("Reboot"), &QAction::triggered, this, &StbItemWidget::reboot);
372 }
373
374 connect(menu.addAction("Check Log"), &QAction::triggered, this, &StbItemWidget::checkLog);
375 connect(menu.addAction("Open Log Directory"), &QAction::triggered, this, &StbItemWidget::openLogDirectory);
376
377 menu.exec(QCursor::pos());
378 qDebug() << "exit menu.";
379 }
380
requestFinished(QNetworkReply * reply)381 void StbItemWidget::requestFinished(QNetworkReply* reply)
382 {
383 // 获取http状态码
384 QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
385 if (statusCode.isValid())
386 qDebug() << "status code=" << statusCode.toInt();
387
388 QVariant reason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
389 if (reason.isValid())
390 qDebug() << "reason=" << reason.toString();
391
392 QNetworkReply::NetworkError err = reply->error();
393 if (err != QNetworkReply::NoError) {
394 qDebug() << "Failed: " << reply->errorString();
395 } else {
396 // 获取返回内容
397 qDebug() << reply->readAll();
398 }
399 }
400
mouseDoubleClickEvent(QMouseEvent * event)401 void StbItemWidget::mouseDoubleClickEvent(QMouseEvent* event)
402 {
403 if (event->button() == Qt::LeftButton) {
404 if (sn.isEmpty()) {
405 playVideo();
406 } else {
407 openConfigWeb();
408 }
409 }
410 }
411
setIndex(int index)412 void StbItemWidget::setIndex(int index)
413 {
414 QString str = QString::asprintf("%d", index);
415 ui->indexLabel->setText(str);
416 }
417
418
setMainInfo(QString mac,QString sn,QString hwv,QString swv,QString dm)419 void StbItemWidget::setMainInfo(QString mac, QString sn, QString hwv, QString swv, QString dm)
420 {
421 ui->macLabel->setText(mac);
422 ui->swLabel->setText(swv);
423 ui->hwLabel->setText(hwv);
424 ui->snLabel->setText(sn);
425 ui->dmLabel->setText(dm);
426 this->mac = mac;
427 this->sn = sn;
428 this->deviceModel = dm;
429 this->hwv = hwv;
430 this->swv = swv;
431 }
432
setIp(QString ip,bool isStatic)433 void StbItemWidget::setIp(QString ip, bool isStatic)
434 {
435 ui->ipLabel->setText(ip + (isStatic ? " (Static)" : ""));
436 this->ip = ip;
437 this->isStaticIP = isStatic;
438 }
439
setTemperature(QString temperature)440 void StbItemWidget::setTemperature(QString temperature)
441 {
442 this->temperature = temperature;
443 }
444
setIpcameraPss(QString ipcameraPss)445 void StbItemWidget::setIpcameraPss(QString ipcameraPss)
446 {
447 this->ipcameraPss = ipcameraPss;
448 }
449
getLogHead()450 QString StbItemWidget::getLogHead()
451 {
452 QString file_head_str = "**************************************************************************\n"
453 "* Mac:\t\t\t"
454 + this->mac
455 + "\n"
456 "* Sn:\t\t\t"
457 + this->sn
458 + "\n"
459 "* Hardware Version:\t"
460 + this->hwv
461 + "\n"
462 "* Software Version:\t"
463 + this->swv
464 + "\n"
465 "* Device Model:\t\t"
466 + this->deviceModel
467 + "\n"
468 "**************************************************************************\n\n";
469 return file_head_str;
470 }
471
logMutexLock()472 void StbItemWidget::logMutexLock()
473 {
474 this->logMutex.lock();
475 }
476
logMutexUnlock()477 void StbItemWidget::logMutexUnlock()
478 {
479 this->logMutex.unlock();
480 }
481
getLogPath()482 QString StbItemWidget::getLogPath()
483 {
484 QString localApplicationDirPath = QCoreApplication::applicationDirPath();
485
486 QString logDir = localApplicationDirPath + "/log/" + (this->sn == NULL ? this->mac : this->sn);
487 QDir dir(logDir);
488 logDir.replace("/", QDir::separator());
489 if (!dir.exists()) {
490 if (!dir.mkpath(logDir)) {
491 return QString();
492 }
493 }
494
495 QString mac = this->mac.replace(":", "-");
496 QString current_date_time = QDateTime::currentDateTime().toString("yyyy-MM-dd");
497 QString logFilePath = this->sn == NULL ? (logDir + "\\" + "log_" + current_date_time + "_" + mac + ".txt")
498 : (logDir + "\\" + "log_" + current_date_time + "_" + this->sn + ".txt");
499 return logFilePath;
500 }
501
writeLog(QString info)502 void StbItemWidget::writeLog(QString info)
503 {
504 if (this->sn == NULL && this->mac == NULL) {
505 return;
506 }
507 QString logFilePath = this->getLogPath();
508
509 QFile file(logFilePath);
510 bool file_exists = file.exists();
511 this->logMutex.lock();
512 if (file.open(QIODevice::WriteOnly | QIODevice::Append)) {
513 if (!file_exists) {
514 QString file_head_str = this->getLogHead();
515 file.write(file_head_str.toStdString().data());
516 }
517 QString current_date_time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
518 QString log_info = "[" + current_date_time + "] " + info + "\r\n";
519 file.write(log_info.toStdString().data());
520 file.flush();
521 file.close();
522 }
523 this->logMutex.unlock();
524 }
525
generateLogInfo()526 QString StbItemWidget::generateLogInfo()
527 {
528 qlonglong secs = (qlonglong)this->upTime.toDouble();
529 QTime time;
530 time.setHMS(0, 0, 0, 0);
531 QString timeStr = time.addSecs(int(secs)).toString("hh:mm:ss");
532 int days = secs / 24 / 60 / 60;
533 QString dt = "";
534 if (days > 0) {
535 dt += QString::number(days) + "天 ";
536 }
537 dt += timeStr;
538
539 QString logInfo = "温度: " + this->temperature + ", Ipcamera_PSS: " + this->ipcameraPss + ", 运行时间: " + dt;
540
541 return logInfo;
542 }
543
setStatusCase1()544 void StbItemWidget::setStatusCase1()
545 {
546 this->setStyleSheet("color:rgb(25, 130, 59)");
547 ui->statusLabel->setText("上线");
548 QString logInfo = "设备上线 ( 当前" + this->generateLogInfo() + " )";
549 this->writeLog(logInfo);
550 }
551
setStatusCase2()552 void StbItemWidget::setStatusCase2()
553 {
554 this->setStyleSheet("color:rgb(223, 63, 33)");
555 ui->statusLabel->setText("离线");
556 if (this->status != DEV_STATUS_OFFLINE) {
557 QString logInfo = "设备离线 ( 最后上报" + this->generateLogInfo() + " )";
558 this->writeLog(logInfo);
559 }
560 }
561
setStatusCase3()562 void StbItemWidget::setStatusCase3()
563 {
564 this->setStyleSheet("color:rgb(132, 112, 255)");
565 ui->statusLabel->setText("正在下载");
566 QString logInfo = "设备正在下载升级包 ( 当前" + this->generateLogInfo() + " )";
567 this->writeLog(logInfo);
568 }
569
setStatusCase4()570 void StbItemWidget::setStatusCase4()
571 {
572 this->setStyleSheet("color:rgb(132, 112, 255)");
573 ui->statusLabel->setText("正在校验");
574 QString logInfo = "设备正在校验升级包 ( 当前" + this->generateLogInfo() + " )";
575 this->writeLog(logInfo);
576 }
577
setStatusCase5()578 void StbItemWidget::setStatusCase5()
579 {
580 this->setStyleSheet("color:rgb(132, 112, 255)");
581 ui->statusLabel->setText("正在更新");
582 QString logInfo = "设备正在更新 ( 当前" + this->generateLogInfo() + " )";
583 this->writeLog(logInfo);
584 }
585
setStatusCase6()586 void StbItemWidget::setStatusCase6()
587 {
588 this->setStyleSheet("color:rgb(226, 190, 27)");
589 ui->statusLabel->setText("下载失败");
590 QString logInfo = "设备下载升级包失败 ( 当前" + this->generateLogInfo() + " )";
591 this->writeLog(logInfo);
592 }
593
setStatus(DEV_STATUS_E myStatus)594 void StbItemWidget::setStatus(DEV_STATUS_E myStatus)
595 {
596 switch (myStatus) {
597 case DEV_STATUS_IDEL: {
598 this->setStatusCase1();
599 break;
600 }
601 case DEV_STATUS_OFFLINE: {
602 this->setStatusCase2();
603 break;
604 }
605 case DEV_STATUS_UPGRADE_DOWNLOADING: {
606 this->setStatusCase3();
607 break;
608 }
609 case DEV_STATUS_UPGRADE_CHECKING: {
610 this->setStatusCase4();
611 break;
612 }
613 case DEV_STATUS_UPGRADE_UPDATING: {
614 this->setStatusCase5();
615 break;
616 }
617 case DEV_STATUS_UPGRADE_ERR_DOWNLOAD: {
618 this->setStatusCase6();
619 break;
620 }
621 case DEV_STATUS_UPGRADE_ERR_CHECK: {
622 this->setStyleSheet("color:rgb(226, 190, 27)");
623 ui->statusLabel->setText("校验失败");
624 QString logInfo = "设备升级包校验失败 ( 当前" + this->generateLogInfo() + " )";
625 this->writeLog(logInfo);
626 break;
627 }
628 case DEV_STATUS_UPGRADE_ERR_UPDATE: {
629 this->setStyleSheet("color:rgb(226, 190, 27)");
630 ;
631 ui->statusLabel->setText("升级失败");
632 QString logInfo = "设备升级失败 ( 当前" + this->generateLogInfo() + " )";
633 this->writeLog(logInfo);
634 break;
635 }
636 default: {
637 this->setStyleSheet("color:rgb(226, 190, 27)");
638 ui->statusLabel->setText("未知");
639 break;
640 }
641 }
642
643 this->status = myStatus;
644 }
645
getStatus()646 DEV_STATUS_E StbItemWidget::getStatus()
647 {
648 return this->status;
649 }
650
setUptime(QString uptime)651 void StbItemWidget::setUptime(QString uptime)
652 {
653 qlonglong secs = (qlonglong)uptime.toDouble();
654 QTime time;
655 time.setHMS(0, 0, 0, 0);
656 QString timeStr = time.addSecs(int(secs)).toString("hh:mm:ss");
657 int days = secs / 24 / 60 / 60;
658 QString dt = "";
659 if (days > 0) {
660 dt += QString::number(days) + "天 ";
661 }
662 dt += timeStr;
663 ui->uptimeLabel->setText(dt);
664
665 if (uptime.toDouble() < this->upTime.toDouble()) {
666 QString logInfo = " 设备重启! ";
667 this->writeLog(logInfo);
668 }
669 this->upTime = uptime;
670 }
671