diff --git a/Application.cpp b/Application.cpp index f1c82c7..0be4d12 100644 --- a/Application.cpp +++ b/Application.cpp @@ -139,11 +139,12 @@ void Application::updateShieldedAreaPoints(const QList &points) { } } -void Application::updateNetworkInfomation(bool dhcp, const QString &ip, const QString &netmask, - const QString &gateway) { +void Application::updateNetworkInfomation(bool dhcp, const QString &ip, const QString &netmask, const QString &gateway, + const QString &dns) { if (!m_device.expired()) { auto device = m_device.lock(); - device->updateNetworkInfomation(dhcp, ip, netmask, gateway); + device->updateNetworkInfomation(dhcp, ip, netmask, gateway, dns); + emit newMessage(1, "网络设置", "设置成功,请重新搜索并连接设备!"); } } @@ -158,11 +159,15 @@ void Application::connectToDevice(int index) { disconnect(device.get(), &DeviceConnection::firmwareChanged, this, &Application::onDeviceFirmware); disconnect(device.get(), &DeviceConnection::otaProgressChanged, this, &Application::currentDeviceOtaProgressChanged); + disconnect(device.get(), &DeviceConnection::connected, this, &Application::onDeviceConnected); + disconnect(device.get(), &DeviceConnection::disconnected, this, &Application::onDeviceDisconnected); + device->setH264FrameCallback(DeviceConnection::H264FrameCallback()); device->setLiveStreamEnabled(false); if (!m_currentFirmware.isEmpty()) { m_currentFirmware.clear(); } + m_currentDeviceConnected = false; } if (index >= 0) { auto device = m_devices->device(index); @@ -176,6 +181,8 @@ void Application::connectToDevice(int index) { connect(device.get(), &DeviceConnection::firmwareChanged, this, &Application::onDeviceFirmware); connect(device.get(), &DeviceConnection::otaProgressChanged, this, &Application::currentDeviceOtaProgressChanged); + connect(device.get(), &DeviceConnection::connected, this, &Application::onDeviceConnected); + connect(device.get(), &DeviceConnection::disconnected, this, &Application::onDeviceDisconnected); device->setH264FrameCallback([this](const char *data, uint32_t size) { auto image = m_player->decode((const uint8_t *)data, size); if (image) { @@ -193,6 +200,7 @@ void Application::connectToDevice(int index) { m_currentAntiClipAreaPoints = area.antiClipArea; m_currentNetworkInfomation = device->networkInfomation(); m_currentFirmware = device->infomation().firmwareVersion; + m_currentDeviceConnected = device->isConnected(); emit currentOpenDoorAreaPointsChanged(); emit currentShieldedAreaPointsChanged(); emit currentAntiClipAreaPointsChanged(); @@ -202,6 +210,7 @@ void Application::connectToDevice(int index) { emit currentNetworkInfomationChanged(); } emit currentFirmwareChanged(); + emit currentDeviceConnectedChanged(); } void Application::startSearchDevice() { @@ -212,7 +221,11 @@ void Application::startSearchDevice() { void Application::upgradeDevice(const QString &file) { if (!m_device.expired()) { auto device = m_device.lock(); - device->requestOta(file); + if (device->isConnected()) { + device->requestOta(file); + } else { + emit newMessage(2, "OTA升级", "设备已离线,请重新连接设备!"); + } } } @@ -243,6 +256,17 @@ void Application::onDeviceFirmware(const QString &firmware) { } } +void Application::onDeviceConnected() { + m_currentDeviceConnected = true; + emit currentDeviceConnectedChanged(); +} + +void Application::onDeviceDisconnected() { + m_currentDeviceConnected = false; + emit currentDeviceConnectedChanged(); + m_collector->stop(); +} + int Application::exec() { QQmlApplicationEngine engine; engine.addImageProvider("videoframe", m_videoFrameProvider); diff --git a/Application.h b/Application.h index f40e4d5..0fefa84 100644 --- a/Application.h +++ b/Application.h @@ -38,6 +38,7 @@ class Application : public QObject { Q_PROPERTY( NetworkInfomation currentNetworkInfomation READ currentNetworkInfomation NOTIFY currentNetworkInfomationChanged) Q_PROPERTY(QString currentFirmware MEMBER m_currentFirmware NOTIFY currentFirmwareChanged) + Q_PROPERTY(bool currentDeviceConnected MEMBER m_currentDeviceConnected NOTIFY currentDeviceConnectedChanged) friend class Amass::Singleton; public: @@ -66,7 +67,7 @@ public: Q_INVOKABLE void updateAntiClipAreaPoints(const QList &points); Q_INVOKABLE void updateShieldedAreaPoints(const QList &points); Q_INVOKABLE void updateNetworkInfomation(bool dhcp, const QString &ip, const QString &netmask, - const QString &gateway); + const QString &gateway, const QString &dns); Q_INVOKABLE void connectToDevice(int index); Q_INVOKABLE void upgradeDevice(const QString &file); Q_INVOKABLE void startSearchDevice(); @@ -84,7 +85,9 @@ signals: void currentAntiClipAreaEnabledChanged(); void currentNetworkInfomationChanged(); void currentFirmwareChanged(); + void currentDeviceConnectedChanged(); void currentDeviceOtaProgressChanged(bool status, int progress, const QString &message); + void newMessage(int type, const QString &title, const QString &message); protected: Application(int &argc, char **argv); @@ -93,6 +96,8 @@ protected: void onDeviceAntiClipArea(bool enabled, const QList &points); void onDeviceNetworkInfomation(const NetworkInfomation &info); void onDeviceFirmware(const QString &firmware); + void onDeviceConnected(); + void onDeviceDisconnected(); private: std::shared_ptr m_app; @@ -110,6 +115,7 @@ private: QList m_currentAntiClipAreaPoints; NetworkInfomation m_currentNetworkInfomation; QString m_currentFirmware; + bool m_currentDeviceConnected = false; }; #endif // APPLICATION_H diff --git a/DataStructure.h b/DataStructure.h index efdc55c..7941b6b 100644 --- a/DataStructure.h +++ b/DataStructure.h @@ -11,11 +11,13 @@ struct NetworkInfomation { Q_PROPERTY(QString ip MEMBER ip) Q_PROPERTY(QString netmask MEMBER netmask) Q_PROPERTY(QString gateway MEMBER gateway) + Q_PROPERTY(QString dns MEMBER dns) public: bool dhcp; QString ip; QString netmask; QString gateway; + QString dns; }; Q_DECLARE_METATYPE(NetworkInfomation) diff --git a/DeviceConnection.cpp b/DeviceConnection.cpp index 51ef701..67fcd04 100644 --- a/DeviceConnection.cpp +++ b/DeviceConnection.cpp @@ -83,7 +83,7 @@ void DeviceConnection::requestOpenDoorArea() { m_requests.push(task); } -void DeviceConnection::updateOpenDoorAreaPoints(AreaWay way, const QList &points) { +QFuture DeviceConnection::updateOpenDoorAreaPoints(AreaWay way, const QList &points) { Task task; task.command = "a03opendoor1_setdata"; task.task = [this, way, points]() { @@ -110,12 +110,14 @@ void DeviceConnection::updateOpenDoorAreaPoints(AreaWay way, const QListwrite(text.data(), text.size()); - LOG(info) << "updateOpenDoorAreaPoints"; }; + task.future = std::make_shared>(); if (m_requests.empty()) { task.task(); } + auto ret = task.future->future(); m_requests.push(task); + return ret; } void DeviceConnection::requestShieldedArea() { @@ -269,11 +271,11 @@ void DeviceConnection::requestNetworkInfomation() { m_requests.push(task); } -void DeviceConnection::updateNetworkInfomation(bool dhcp, const QString &ip, const QString &netmask, - const QString &gateway) { +QFuture DeviceConnection::updateNetworkInfomation(bool dhcp, const QString &ip, const QString &netmask, + const QString &gateway, const QString &dns) { Task task; task.command = "netconfig_setdata"; - task.task = [this, dhcp, ip, netmask, gateway]() { + task.task = [this, dhcp, ip, netmask, gateway, dns]() { boost::json::object request; request["func"] = "netconfig_setdata"; request["deviceid"] = "0"; @@ -282,16 +284,20 @@ void DeviceConnection::updateNetworkInfomation(bool dhcp, const QString &ip, con data["ip"] = ip.toStdString(); data["netmask"] = netmask.toStdString(); data["gateway"] = gateway.toStdString(); + data["dns"] = dns.toStdString(); request["data"] = std::move(data); auto text = boost::json::serialize(request); m_commandSocket->write(text.data(), text.size()); LOG(info) << "requestUpdateNetworkInfomation"; }; + task.future = std::make_shared>(); if (m_requests.empty()) { task.task(); } + auto ret = task.future->future(); m_requests.push(task); + return ret; } void DeviceConnection::requestOta(const QString &file) { @@ -359,7 +365,7 @@ void DeviceConnection::transferBinContent() { m_sendedSize += sendSize; auto fileProgress = static_cast(m_sendedSize) / m_uploadBuffer.size(); - m_otaProgress = 2 + 95 * fileProgress; + m_otaProgress = 2 + 96 * fileProgress; emit otaProgressChanged(true, m_otaProgress, "向设备发送OTA升级文件......"); if (m_sendedSize < m_uploadBuffer.size()) { @@ -381,7 +387,7 @@ void DeviceConnection::transferBinContent() { } } -QString DeviceConnection::handleCommand(const std::string_view &replyText) { +QString DeviceConnection::handleCommand(const std::string_view &replyText, const Task *task) { QString ret; boost::system::error_code error; auto replyValue = boost::json::parse(replyText, error); @@ -448,7 +454,11 @@ QString DeviceConnection::handleCommand(const std::string_view &replyText) { m_networkInfomation.ip = data.at("ip").as_string().c_str(); m_networkInfomation.gateway = data.at("gateway").as_string().c_str(); m_networkInfomation.netmask = data.at("netmask").as_string().c_str(); + if (data.contains("dns")) { + m_networkInfomation.dns = data.at("dns").as_string().c_str(); + } emit networkInfomationChanged(m_networkInfomation); + LOG(info) << replyText; } else if (function == "a15devicedetail_getdata") { auto &data = reply.at("data").as_object(); auto firmware = QString::fromStdString(std::string(data.at("linux04_firmware").as_string())); @@ -461,7 +471,27 @@ QString DeviceConnection::handleCommand(const std::string_view &replyText) { } else if (function == "a03opendoor4_setdata") { requestShieldedArea(); } else if (function == "a03opendoor1_setdata") { + if ((task != nullptr) && (task->command.toStdString() == function)) { + if (task->timeoutTimer) { + task->timeoutTimer->stop(); + } + bool status = true; + if (task->future) { + task->future->reportFinished(&status); + } + } requestOpenDoorArea(); + } else if (function == "netconfig_setdata") { + if ((task != nullptr) && (task->command.toStdString() == function)) { + if (task->timeoutTimer) { + task->timeoutTimer->stop(); + } + bool status = true; + if (task->future) { + task->future->reportFinished(&status); + } + } + LOG(info) << replyText; } else if (function == "a22devicefirmware_setdata") { LOG(warning) << "ota reply: " << replyText; auto &data = reply.at("data").as_object(); @@ -472,18 +502,33 @@ QString DeviceConnection::handleCommand(const std::string_view &replyText) { QTimer::singleShot(0, this, [this]() { transferBinContent(); }); } else if (value == "2") { LOG(info) << "md5 check finished"; - m_otaProgress = 98; + m_otaProgress = 99; emit otaProgressChanged(true, m_otaProgress, "设备正在升级中,请稍后......"); QTimer::singleShot(0, this, [this]() { m_commandSocket->close(); m_h264Socket->close(); + if (m_otaTimer == nullptr) { + m_otaTimer = new QTimer(this); + m_otaTimer->setSingleShot(true); + } + m_otaTimer->callOnTimeout( + this, + [this]() { + emit otaProgressChanged(false, m_otaProgress, "升级超时,请检查设备并重新尝试..."); + m_otaProgress = -1; + m_commandSocket->close(); + m_h264Socket->close(); + }, + Qt::SingleShotConnection); + m_otaTimer->start(120 * 1000); }); QTimer::singleShot(25000, this, [this]() { LOG(info) << "try connect after ota."; m_commandSocket->connectToHost(m_infomation.ip, 8000); m_h264Socket->connectToHost(m_infomation.ip, 8000); - m_needReconnect = true; }); + } else { + emit otaProgressChanged(false, m_otaProgress, QString("升级失败,错误码: %1").arg(value.c_str())); } } else { LOG(warning) << "unknown reply: " << replyText; @@ -501,12 +546,15 @@ void DeviceConnection::onConnected() { requestNetworkInfomation(); emit connected(); m_timerId = startTimer(2500); - if (m_otaProgress > 0) { - m_otaProgress = 100; - emit otaProgressChanged(true, m_otaProgress, "设备升级成功!"); + if (m_otaProgress == 99) { + m_otaProgress = -1; + emit otaProgressChanged(true, 100, "设备升级成功!"); + } + if (m_otaTimer != nullptr) { + m_otaTimer->stop(); } } else if (socket == m_h264Socket) { - if (m_needReconnect) { + if (m_otaProgress >= 99) { setLiveStreamEnabled(true); } } @@ -520,6 +568,10 @@ void DeviceConnection::onDisconnected() { m_timerId = -1; } emit disconnected(); + if ((m_otaProgress >= 0) && (m_otaProgress <= 98)) { + m_otaProgress = -1; + emit otaProgressChanged(false, m_otaProgress, "网络断开,设备升级失败!"); + } } } @@ -555,7 +607,8 @@ void DeviceConnection::onCommandReadyRead() { while (!m_commandBuffer.isEmpty()) { auto packageSize = ntohl(*reinterpret_cast(m_commandBuffer.data())); if (m_commandBuffer.size() < (packageSize + sizeof(uint32_t))) break; - auto command = handleCommand(std::string_view(m_commandBuffer.data() + sizeof(uint32_t), packageSize)); + auto command = handleCommand(std::string_view(m_commandBuffer.data() + sizeof(uint32_t), packageSize), + m_requests.empty() ? nullptr : &m_requests.front()); m_commandBuffer.remove(0, packageSize + sizeof(uint32_t)); if (!m_requests.empty()) { @@ -573,7 +626,7 @@ void DeviceConnection::onCommandReadyRead() { void DeviceConnection::onErrorOccurred(QAbstractSocket::SocketError socketError) { auto socket = dynamic_cast(sender()); qDebug() << "DeviceConnection::onErrorOccurred" << socketError; - if (m_needReconnect) { + if (m_otaProgress == 99) { // 99 表示设备正在OTA重启,需要重连 if (socket->state() == QTcpSocket::UnconnectedState) { LOG(info) << "try reconnect after ota."; socket->connectToHost(m_infomation.ip, 8000); diff --git a/DeviceConnection.h b/DeviceConnection.h index d351cc3..93eee65 100644 --- a/DeviceConnection.h +++ b/DeviceConnection.h @@ -2,6 +2,8 @@ #define DEVICECONNECTION_H #include "DataStructure.h" +#include +#include #include #include #include @@ -59,22 +61,23 @@ public: Area area() const; void requestOpenDoorArea(); - void updateOpenDoorAreaPoints(AreaWay way, const QList &points); + QFuture updateOpenDoorAreaPoints(AreaWay way, const QList &points); void requestShieldedArea(); void updateShieldedAreaPoints(bool enabled, const QList &points); void requestAntiClipArea(); void updateAntiClipAreaPoints(bool enabled, const QList &points); void requestResolution(Resolution resolution); void requestNetworkInfomation(); - void updateNetworkInfomation(bool dhcp, const QString &ip, const QString &netmask, const QString &gateway); + QFuture updateNetworkInfomation(bool dhcp, const QString &ip, const QString &netmask, const QString &gateway, + const QString &dns); void requestVersion(); /** * @brief 对设备升级OTA,主要有几个步骤 * 1. 对设备发起OTA请求,等待设备回复 1 - * 2. 发送二进制文件至设备 2-97 - * 3. 对设备发起包检查结果,等待设备回复 98 - * 4. 设备会重启,一直尝试连接设置,直至连接成功 99 + * 2. 发送二进制文件至设备 2-98 + * 3. 对设备发起包检查结果,等待设备回复 99 + * 4. 设备会重启,一直尝试连接设置,直至连接成功 100 * * @param file */ @@ -91,11 +94,18 @@ signals: void otaProgressChanged(bool status, int progress, const QString &message); protected: + class Task { + public: + QString command; + std::function task; + std::shared_ptr timeoutTimer = nullptr; + std::shared_ptr> future; + }; void onConnected(); void onDisconnected(); void onH264ReadyRead(); void onCommandReadyRead(); - QString handleCommand(const std::string_view &replyText); + QString handleCommand(const std::string_view &replyText, const Task *task); void onErrorOccurred(QAbstractSocket::SocketError socketError); void timerEvent(QTimerEvent *event) final; void transferBinContent(); @@ -110,17 +120,12 @@ private: QByteArray m_commandBuffer; QByteArray m_h264Buffer; - int m_otaProgress = 0; - bool m_needReconnect = false; + int m_otaProgress = -1; std::vector m_uploadBuffer; int m_sendedSize = 0; + QTimer *m_otaTimer = nullptr; // 检测OTA超时 H264FrameCallback m_frameCallback; - class Task { - public: - QString command; - std::function task; - }; std::queue m_requests; int m_timerId = -1; int heartbeats = 0; diff --git a/qml/IpTextField.qml b/qml/IpTextField.qml index 2d4ba4a..ffaab88 100644 --- a/qml/IpTextField.qml +++ b/qml/IpTextField.qml @@ -12,11 +12,15 @@ Column { id: input onTextChanged: { - valid = validateIp(text); - if(!valid){ - hint.text="参数配置无效" + if(text.length<=0){ + hint.text = "参数不能为空" + return + } + valid = validateIp(text) + if (!valid) { + hint.text = "参数配置无效" } else { - hint.text="" + hint.text = "" } } } @@ -28,7 +32,10 @@ Column { function validateIp(ip) { // Regular expression for validating IP address - var regex = /^(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$/; - return regex.test(ip); + var regex = /^(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$/ + return regex.test(ip) + } + function reset() { + hint.text = "" } } diff --git a/qml/Main.qml b/qml/Main.qml index 1c4a60b..0020ed3 100644 --- a/qml/Main.qml +++ b/qml/Main.qml @@ -95,7 +95,7 @@ ApplicationWindow { anchors.bottom: parent.bottom anchors.left: deviceList.right anchors.right: parent.right - enabled: deviceList.currentIndex >= 0 + enabled: App.currentDeviceConnected&&(deviceList.currentIndex >= 0) openDoorAreaWay: App.currentOpenDoorAreaWay openDoorAreaPoints: App.currentOpenDoorAreaPoints shieldedAreaEnabled: App.currentShieldedAreaEnabled @@ -134,6 +134,9 @@ ApplicationWindow { if (deviceList.currentIndex < 0) { showMessageDialog(2, "数据采集", "请先选择设备") return + } else if(!App.currentDeviceConnected){ + showMessageDialog(2, "数据采集", "设备已离线,请重新连接设备!") + return } if (App.collector.path.length <= 0) { folderDialog.open() @@ -149,7 +152,10 @@ ApplicationWindow { text: "升级" onClicked: { if (deviceList.currentIndex < 0) { - showMessageDialog(2, "OTA升级", "请选选择设备") + showMessageDialog(2, "OTA升级", "请先选择设备!") + return + }else if(!App.currentDeviceConnected){ + showMessageDialog(2, "OTA升级", "设备已离线,请重新连接设备!") return } otaPopup.open() @@ -171,4 +177,10 @@ ApplicationWindow { dialog.open() } } + Connections { + target: App + function onNewMessage(type, title, message) { + showMessageDialog(type, title, message) + } + } } diff --git a/qml/MessageDialog.qml b/qml/MessageDialog.qml index 1df78bc..81d5958 100644 --- a/qml/MessageDialog.qml +++ b/qml/MessageDialog.qml @@ -102,7 +102,7 @@ Dialog { } else if (type === MessageDialog.Type.Warning) { image.source = "qrc:/qt/qml/AntiClipSettings/resources/warning.svg" } else if(type === MessageDialog.Type.Failed){ - // image.source = "qrc:/AntiClipSettings/resources/prompt_delete.svg" + image.source = "qrc:/qt/qml/AntiClipSettings/resources/prompt_delete.svg" } } } diff --git a/qml/NetworkSettingPopup.qml b/qml/NetworkSettingPopup.qml index 2d918ea..d433c23 100644 --- a/qml/NetworkSettingPopup.qml +++ b/qml/NetworkSettingPopup.qml @@ -33,8 +33,7 @@ Popup { RadioButton { id: staticMode text: "静态IP" - checked: true - // checked: !App.currentNetworkInfomation.dhcp + checked: !App.currentNetworkInfomation.dhcp } } @@ -106,7 +105,7 @@ Popup { id: dnsInput width: 350 height: inputHeight - text: App.currentNetworkInfomation.gateway + text: App.currentNetworkInfomation.dns } } @@ -125,7 +124,7 @@ Popup { App.updateNetworkInfomation(dhcpMode.checked, ipInput.text, netmaskInput.text, - gatewayInput.text) + gatewayInput.text,dnsInput.text) networkPopup.close() } else { showMessageDialog(2, "网络设置", "请输入合法参数地址!") @@ -139,4 +138,10 @@ Popup { } } } + onVisibleChanged: { + ipInput.reset() + netmaskInput.reset() + gatewayInput.reset() + dnsInput.reset() + } } diff --git a/qml/OtaPopup.qml b/qml/OtaPopup.qml index b72279b..6a23c2f 100644 --- a/qml/OtaPopup.qml +++ b/qml/OtaPopup.qml @@ -107,15 +107,19 @@ Popup { Connections { target: App function onCurrentDeviceOtaProgressChanged (status, progress, message) { + if(progress<0)progress=0 otaFinished = !status || (progress>=100) progressBar.value = progress progressText.text = `${progress}%` + otaMessage.text = message if(progress>=100){ - otaMessage.text = "OTA升级完成" otaMessage.color = "green" - }else { - otaMessage.text = message } } } + onVisibleChanged: { + if(!visible){ + otaMessage.color = "black" + } + } }