适配设备的上线策略。

This commit is contained in:
luocai 2024-08-22 18:12:11 +08:00
parent 31aa3d86ac
commit d822dcda76
5 changed files with 113 additions and 53 deletions

View File

@ -145,7 +145,7 @@ void Application::updateNetworkInfomation(bool dhcp, const QString &ip, const QS
if (!m_device.expired()) { if (!m_device.expired()) {
auto device = m_device.lock(); auto device = m_device.lock();
device->updateNetworkInfomation(dhcp, ip, netmask, gateway, dns); device->updateNetworkInfomation(dhcp, ip, netmask, gateway, dns);
emit newMessage(1, "网络设置", "设置成功,请重新搜索并连接设备!"); emit newMessage(1, "网络设置", "设置成功,请等待设备重新上线!");
} }
} }

View File

@ -28,6 +28,15 @@ bool DeviceConnection::isConnected() const {
} }
void DeviceConnection::connect(const Infomation &infomation) { void DeviceConnection::connect(const Infomation &infomation) {
while (!m_requests.empty()) {
m_requests.pop();
}
if (m_commandSocket != nullptr) {
m_commandSocket->deleteLater();
}
if (m_h264Socket != nullptr) {
m_h264Socket->deleteLater();
}
m_infomation = infomation; m_infomation = infomation;
m_commandSocket = new QTcpSocket(this); m_commandSocket = new QTcpSocket(this);
QObject::connect(m_commandSocket, &QTcpSocket::disconnected, this, &DeviceConnection::onDisconnected); QObject::connect(m_commandSocket, &QTcpSocket::disconnected, this, &DeviceConnection::onDisconnected);
@ -54,6 +63,7 @@ NetworkInfomation DeviceConnection::networkInfomation() const {
} }
void DeviceConnection::setLiveStreamEnabled(bool enabled) { void DeviceConnection::setLiveStreamEnabled(bool enabled) {
m_videoEnabled = enabled;
boost::json::object request; boost::json::object request;
request["func"] = enabled ? "openlivestream_setdata" : "closelivestream_setdata"; request["func"] = enabled ? "openlivestream_setdata" : "closelivestream_setdata";
request["deviceid"] = "0"; request["deviceid"] = "0";
@ -365,7 +375,9 @@ void DeviceConnection::transferBinContent() {
auto fileProgress = static_cast<float>(m_sendedSize) / m_uploadBuffer.size(); auto fileProgress = static_cast<float>(m_sendedSize) / m_uploadBuffer.size();
m_otaProgress = 2 + 96 * fileProgress; m_otaProgress = 2 + 96 * fileProgress;
emit otaProgressChanged(true, m_otaProgress, "向设备发送OTA升级文件......"); emit otaProgressChanged(true, m_otaProgress,
m_otaProgress < 98 ? "向设备发送升级固件......"
: "升级固件发送完成,等待设备校验升级固件......");
if (m_sendedSize < m_uploadBuffer.size()) { if (m_sendedSize < m_uploadBuffer.size()) {
QTimer::singleShot(0, this, &DeviceConnection::transferBinContent); QTimer::singleShot(0, this, &DeviceConnection::transferBinContent);
@ -490,6 +502,7 @@ QString DeviceConnection::handleCommand(const std::string_view &replyText, const
task->future->reportFinished(&status); task->future->reportFinished(&status);
} }
} }
requestNetworkInfomation();
LOG(info) << replyText; LOG(info) << replyText;
} else if (function == "a22devicefirmware_setdata") { } else if (function == "a22devicefirmware_setdata") {
LOG(warning) << "ota reply: " << replyText; LOG(warning) << "ota reply: " << replyText;
@ -497,14 +510,14 @@ QString DeviceConnection::handleCommand(const std::string_view &replyText, const
auto &value = data.at("value").as_string(); auto &value = data.at("value").as_string();
if (value == "1") { if (value == "1") {
m_otaProgress = 1; m_otaProgress = 1;
emit otaProgressChanged(true, m_otaProgress, "设备已进入OTA升级状态......"); emit otaProgressChanged(true, m_otaProgress, "设备已进入升级状态......");
QTimer::singleShot(0, this, [this]() { transferBinContent(); }); QTimer::singleShot(0, this, [this]() { transferBinContent(); });
} else if (value == "2") { } else if (value == "2") {
LOG(info) << "md5 check finished"; LOG(info) << "md5 check finished";
m_otaProgress = 99; m_otaProgress = 99;
emit otaProgressChanged(true, m_otaProgress, "设备正在升级中,请稍后......"); emit otaProgressChanged(true, m_otaProgress, "设备正在升级中,请稍后......");
QTimer::singleShot(0, this, [this]() { QTimer::singleShot(0, this, [this]() {
m_commandSocket->close(); m_commandSocket->close(); // 等待设备重新上线后,发起广播。由搜索服务触发重连
m_h264Socket->close(); m_h264Socket->close();
if (m_otaTimer == nullptr) { if (m_otaTimer == nullptr) {
m_otaTimer = new QTimer(this); m_otaTimer = new QTimer(this);
@ -519,12 +532,7 @@ QString DeviceConnection::handleCommand(const std::string_view &replyText, const
m_h264Socket->close(); m_h264Socket->close();
}, },
Qt::SingleShotConnection); Qt::SingleShotConnection);
m_otaTimer->start(60 * 1000); m_otaTimer->start(60 * 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);
}); });
} else { } else {
const char *message = nullptr; const char *message = nullptr;
@ -569,7 +577,7 @@ void DeviceConnection::onConnected() {
m_otaTimer->stop(); m_otaTimer->stop();
} }
} else if (socket == m_h264Socket) { } else if (socket == m_h264Socket) {
if (m_otaProgress >= 99) { if (m_videoEnabled) {
setLiveStreamEnabled(true); setLiveStreamEnabled(true);
} }
} }
@ -640,13 +648,7 @@ void DeviceConnection::onCommandReadyRead() {
void DeviceConnection::onErrorOccurred(QAbstractSocket::SocketError socketError) { void DeviceConnection::onErrorOccurred(QAbstractSocket::SocketError socketError) {
auto socket = dynamic_cast<QTcpSocket *>(sender()); auto socket = dynamic_cast<QTcpSocket *>(sender());
qDebug() << "DeviceConnection::onErrorOccurred" << socketError; LOG(info) << "DeviceConnection::onErrorOccurred" << socketError;
if (m_otaProgress == 99) { // 99 表示设备正在OTA重启需要重连
if (socket->state() == QTcpSocket::UnconnectedState) {
LOG(info) << "try reconnect after ota.";
socket->connectToHost(m_infomation.ip, 8000);
}
}
} }
void DeviceConnection::timerEvent(QTimerEvent *event) { void DeviceConnection::timerEvent(QTimerEvent *event) {

View File

@ -18,6 +18,7 @@ class DeviceConnection : public QObject {
QML_UNCREATABLE("Only created in C++...") QML_UNCREATABLE("Only created in C++...")
public: public:
constexpr static auto WirelessAddress = "192.168.10.2";
enum Resolution { enum Resolution {
Video_360P = 0, Video_360P = 0,
Video_720P, Video_720P,
@ -115,6 +116,7 @@ private:
QTcpSocket *m_commandSocket = nullptr; QTcpSocket *m_commandSocket = nullptr;
QTcpSocket *m_h264Socket = nullptr; QTcpSocket *m_h264Socket = nullptr;
bool m_videoEnabled = false;
bool m_receivedFirstJsonReply = false; bool m_receivedFirstJsonReply = false;
QByteArray m_commandBuffer; QByteArray m_commandBuffer;

View File

@ -7,6 +7,38 @@
#include <QUdpSocket> #include <QUdpSocket>
#include <boost/json/parse.hpp> #include <boost/json/parse.hpp>
static QHostAddress calculateNetworkAddress(const QHostAddress &ip, const QHostAddress &netmask) {
quint32 ipInt = ip.toIPv4Address();
quint32 netmaskInt = netmask.toIPv4Address();
quint32 networkInt = ipInt & netmaskInt;
return QHostAddress(networkInt);
}
static bool isSameNetwork(const QHostAddress &ip1, const QHostAddress &netmask1, const QHostAddress &ip2) {
QHostAddress network1 = calculateNetworkAddress(ip1, netmask1);
QHostAddress network2 = calculateNetworkAddress(ip2, netmask1); // Using the same netmask for the target IP
return network1 == network2;
}
static bool isTargetIPInSameNetwork(const QHostAddress &targetIP) {
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
for (const QNetworkInterface &iface : interfaces) {
QList<QNetworkAddressEntry> entries = iface.addressEntries();
for (const QNetworkAddressEntry &entry : entries) {
QHostAddress ip = entry.ip();
QHostAddress netmask = entry.netmask();
if (ip.protocol() == QAbstractSocket::IPv4Protocol) {
bool sameNetwork = isSameNetwork(ip, netmask, targetIP);
if (sameNetwork) {
return true;
}
}
}
}
return false;
}
DeviceListModel::DeviceListModel(QObject *parent) : QAbstractListModel{parent} { DeviceListModel::DeviceListModel(QObject *parent) : QAbstractListModel{parent} {
m_broadcastSocket = new QUdpSocket(this); m_broadcastSocket = new QUdpSocket(this);
} }
@ -72,6 +104,12 @@ void DeviceListModel::startSearchDevice() {
LOG(error) << "app is searching device."; LOG(error) << "app is searching device.";
return; return;
} }
if (!m_udpSockets.empty()) {
for (auto &socket : m_udpSockets) {
socket->deleteLater();
}
m_udpSockets.clear();
}
beginResetModel(); beginResetModel();
m_devices.clear(); m_devices.clear();
endResetModel(); endResetModel();
@ -143,37 +181,56 @@ void DeviceListModel::onDeviceReplyReadyRead() {
auto udp = dynamic_cast<QUdpSocket *>(sender()); auto udp = dynamic_cast<QUdpSocket *>(sender());
while (udp->hasPendingDatagrams()) { while (udp->hasPendingDatagrams()) {
QNetworkDatagram datagram = udp->receiveDatagram(); QNetworkDatagram datagram = udp->receiveDatagram();
auto replyVale = boost::json::parse(datagram.data().toStdString()); if (isTargetIPInSameNetwork(datagram.senderAddress())) { // 设备开机时可能会广播一个 192.168.2.10 的地址
auto &reply = replyVale.as_object(); auto replyVale = boost::json::parse(datagram.data().toStdString());
DeviceConnection::Infomation device; auto &reply = replyVale.as_object();
if (reply.contains("devid")) { DeviceConnection::Infomation device;
device.deviceId = QString::fromStdString(std::string(reply.at("devid").as_string())); if (reply.contains("devid")) {
} device.deviceId = QString::fromStdString(std::string(reply.at("devid").as_string()));
if (reply.contains("fw_ver")) { }
device.firmwareVersion = QString::fromStdString(std::string(reply.at("fw_ver").as_string())); if (reply.contains("fw_ver")) {
} device.firmwareVersion = QString::fromStdString(std::string(reply.at("fw_ver").as_string()));
if (reply.contains("sw_ver")) { }
device.softwareVersion = QString::fromStdString(std::string(reply.at("sw_ver").as_string())); if (reply.contains("sw_ver")) {
} device.softwareVersion = QString::fromStdString(std::string(reply.at("sw_ver").as_string()));
if (!device.softwareVersion.startsWith("RD_T009")) continue; // 其它设备不予显示 }
device.ip = datagram.senderAddress().toString(); if (!device.softwareVersion.startsWith("RD_T009")) continue; // 其它设备不予显示
device.ip = datagram.senderAddress().toString();
auto iterator = std::find_if(m_devices.cbegin(), m_devices.cend(), auto iterator = std::find_if(m_devices.cbegin(), m_devices.cend(),
[&device](const std::shared_ptr<DeviceConnection> &item) { [&device](const std::shared_ptr<DeviceConnection> &item) {
return item->infomation().deviceId == device.deviceId; return item->infomation().deviceId == device.deviceId;
}); });
if (iterator == m_devices.cend()) { if (iterator == m_devices.cend()) {
auto connection = std::shared_ptr<DeviceConnection>(new DeviceConnection(), if (m_timerId >= 0) { // 只有在搜索设备的过程中,才加入新设备
[](DeviceConnection *self) { self->deleteLater(); }); auto connection = std::shared_ptr<DeviceConnection>(
connect(connection.get(), &DeviceConnection::connected, this, &DeviceListModel::onDeviceConnected); new DeviceConnection(), [](DeviceConnection *self) { self->deleteLater(); });
connect(connection.get(), &DeviceConnection::disconnected, this, &DeviceListModel::onDeviceDisconnected); connect(connection.get(), &DeviceConnection::connected, this, &DeviceListModel::onDeviceConnected);
connection->connect(device); connect(connection.get(), &DeviceConnection::disconnected, this,
beginInsertRows(QModelIndex(), m_devices.size(), m_devices.size()); &DeviceListModel::onDeviceDisconnected);
m_devices.push_back(connection); connection->connect(device);
endInsertRows(); beginInsertRows(QModelIndex(), m_devices.size(), m_devices.size());
m_devices.push_back(connection);
endInsertRows();
}
} else {
auto info = (*iterator)->infomation();
if ((m_timerId < 0) ||
((info.ip == DeviceConnection::WirelessAddress) && (device.ip != DeviceConnection::WirelessAddress)) ||
((device.ip != DeviceConnection::WirelessAddress) && (device.ip != info.ip))) {
QList<int> roles;
roles << OnlineStatusRole << IpRole;
int row = std::distance(m_devices.cbegin(), iterator);
emit dataChanged(index(row), index(row), roles);
(*iterator)->connect(device);
LOG(info) << "device: " << device.deviceId.toStdString() << " back online";
}
}
} }
LOG(info) << "Received datagram from " << datagram.senderAddress().toString().toStdString() << ":" LOG(info) << datagram.destinationAddress().toString().toStdString() << ":" << datagram.destinationPort()
<< datagram.senderPort() << ", Data: " << datagram.data().toStdString(); << " received datagram from " << datagram.senderAddress().toString().toStdString() << ":"
<< datagram.senderPort() << ", Data: " << datagram.data().toStdString()
<< ", same network: " << isTargetIPInSameNetwork(datagram.senderAddress());
} }
} }
@ -201,12 +258,6 @@ void DeviceListModel::stopSearchDevice() {
killTimer(m_timerId); killTimer(m_timerId);
m_timerId = -1; m_timerId = -1;
} }
if (!m_udpSockets.empty()) {
for (auto &socket : m_udpSockets) {
socket->deleteLater();
}
m_udpSockets.clear();
}
emit isSearchingChanged(); emit isSearchingChanged();
} }

View File

@ -45,6 +45,7 @@ Popup {
placeholderText: "请选择升级bin文件" placeholderText: "请选择升级bin文件"
} }
Button { Button {
enabled: otaFinished
text: "选择" text: "选择"
onClicked: fileDialog.open() onClicked: fileDialog.open()
} }
@ -76,6 +77,7 @@ Popup {
Layout.fillWidth: true Layout.fillWidth: true
} }
Button { Button {
enabled: otaFinished
text: "开始" text: "开始"
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
onClicked: { onClicked: {
@ -119,7 +121,10 @@ Popup {
} }
onVisibleChanged: { onVisibleChanged: {
if(!visible){ if(!visible){
progressBar.value = 0
otaMessage.color = "black" otaMessage.color = "black"
progressText.text = "0%"
otaMessage.text = "请选择升级文件,点击开始按钮升级模组"
} }
} }
} }