#include "DeviceListModel.h" #include "BoostLog.h" #include "DeviceConnection.h" #include #include #include #include #include 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 interfaces = QNetworkInterface::allInterfaces(); for (const QNetworkInterface &iface : interfaces) { QList 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} { m_broadcastSocket = new QUdpSocket(this); } int DeviceListModel::rowCount(const QModelIndex &parent) const { return m_devices.size(); } QVariant DeviceListModel::data(const QModelIndex &index, int role) const { QVariant ret; auto row = index.row(); if (row >= m_devices.size()) { return ret; } auto info = m_devices.at(row)->infomation(); if (role == DeviceIdRole) { ret = info.deviceId; } else if (role == FirmwareVersionRole) { ret = info.firmwareVersion; } else if (role == SoftwareVersionRole) { ret = info.softwareVersion; } else if (role == IpRole) { ret = info.ip; } else if (role == OnlineStatusRole) { ret = m_devices.at(row)->isConnected(); } return ret; } QHash DeviceListModel::roleNames() const { QHash roleNames; roleNames.insert(DeviceIdRole, "deviceId"); roleNames.insert(FirmwareVersionRole, "firmwareVersion"); roleNames.insert(SoftwareVersionRole, "softwareVersion"); roleNames.insert(IpRole, "ip"); roleNames.insert(OnlineStatusRole, "onlineStatus"); return roleNames; } QVariantMap DeviceListModel::get(int index) const { QVariantMap map; if (index >= 0 && index < m_devices.size()) { auto info = m_devices.at(index)->infomation(); map["firmwareVersion"] = info.firmwareVersion; map["softwareVersion"] = info.softwareVersion; map["deviceId"] = info.deviceId; map["ip"] = info.ip; map["onlineStatus"] = m_devices.at(index)->isConnected(); } return map; } std::shared_ptr DeviceListModel::device(int index) { std::shared_ptr ret; if (index < m_devices.size()) { ret = m_devices.at(index); } return ret; } void DeviceListModel::startSearchDevice() { if (m_timerId >= 0) { LOG(error) << "app is searching device."; return; } if (!m_udpSockets.empty()) { for (auto &socket : m_udpSockets) { socket->deleteLater(); } m_udpSockets.clear(); } beginResetModel(); m_devices.clear(); endResetModel(); auto interfaces = QNetworkInterface::allInterfaces(); for (auto &interface : interfaces) { if (interface.flags() & QNetworkInterface::IsLoopBack) continue; if (interface.flags() & QNetworkInterface::IsUp && interface.flags() & QNetworkInterface::IsRunning) { const QList entries = interface.addressEntries(); for (const QNetworkAddressEntry &entry : entries) { if (entry.ip().protocol() != QAbstractSocket::IPv4Protocol) continue; QUdpSocket *socket = new QUdpSocket(this); if (socket->bind(entry.ip(), ListenPort, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)) { connect(socket, &QUdpSocket::readyRead, this, &DeviceListModel::onDeviceReplyReadyRead); m_udpSockets.push_back(socket); LOG(info) << "Listening on " << entry.ip().toString().toStdString() << ":" << ListenPort; } else { LOG(error) << "Failed to bind UDP socket on" << entry.ip().toString().toStdString() << ":" << ListenPort; delete socket; } } } } if (!m_udpSockets.empty()) { m_retries = 0; emit searchProgressChanged(); m_timerId = startTimer(1000); emit isSearchingChanged(); } } bool DeviceListModel::isSearching() const { return m_timerId >= 0; } float DeviceListModel::searchProgress() const { return static_cast(m_retries) * 100 / RetryCount; } void DeviceListModel::onDeviceConnected() { auto device = dynamic_cast(sender()); auto iterator = std::find_if(m_devices.cbegin(), m_devices.cend(), [device](const std::shared_ptr &item) { return item.get() == device; }); if (iterator != m_devices.cend()) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QList roles; #else QVector roles; #endif roles << OnlineStatusRole; int row = std::distance(m_devices.cbegin(), iterator); emit dataChanged(index(row), index(row), roles); LOG(info) << "device " << row << " connected."; } } void DeviceListModel::onDeviceDisconnected() { auto device = dynamic_cast(sender()); auto iterator = std::find_if(m_devices.cbegin(), m_devices.cend(), [device](const std::shared_ptr &item) { return item.get() == device; }); if (iterator != m_devices.cend()) { int row = std::distance(m_devices.cbegin(), iterator); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QList roles; #else QVector roles; #endif roles << OnlineStatusRole; emit dataChanged(index(row), index(row), roles); LOG(info) << "device " << row << " disconnected."; } } void DeviceListModel::onDeviceReplyReadyRead() { auto udp = dynamic_cast(sender()); while (udp->hasPendingDatagrams()) { QNetworkDatagram datagram = udp->receiveDatagram(); if (isTargetIPInSameNetwork(datagram.senderAddress())) { // 设备开机时可能会广播一个 192.168.2.10 的地址 auto replyVale = boost::json::parse(datagram.data().toStdString()); auto &reply = replyVale.as_object(); DeviceConnection::Infomation device; 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("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(); auto iterator = std::find_if(m_devices.cbegin(), m_devices.cend(), [&device](const std::shared_ptr &item) { return item->infomation().deviceId == device.deviceId; }); if (iterator == m_devices.cend()) { if (m_timerId >= 0) { // 只有在搜索设备的过程中,才加入新设备 auto connection = std::shared_ptr( new DeviceConnection(), [](DeviceConnection *self) { self->deleteLater(); }); connect(connection.get(), &DeviceConnection::connected, this, &DeviceListModel::onDeviceConnected); connect(connection.get(), &DeviceConnection::disconnected, this, &DeviceListModel::onDeviceDisconnected); connection->connect(device); 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))) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QList roles; #else QVector roles; #endif 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) << datagram.destinationAddress().toString().toStdString() << ":" << datagram.destinationPort() << " received datagram from " << datagram.senderAddress().toString().toStdString() << ":" << datagram.senderPort() << ", Data: " << datagram.data().toStdString() << ", same network: " << isTargetIPInSameNetwork(datagram.senderAddress()); } } void DeviceListModel::broadcast() { if (m_udpSockets.empty()) return; QByteArray datagram = "FACEPASS_V2"; auto interfaces = QNetworkInterface::allInterfaces(); for (auto &interface : interfaces) { if (interface.flags() & QNetworkInterface::IsLoopBack) continue; if (interface.flags() & QNetworkInterface::IsUp && interface.flags() & QNetworkInterface::IsRunning) { const QList entries = interface.addressEntries(); for (const QNetworkAddressEntry &entry : entries) { if (entry.broadcast().toIPv4Address()) { m_broadcastSocket->writeDatagram(datagram, entry.broadcast(), BroadcastPort); // LOG(info) << "Broadcasted datagram: " << datagram.toStdString() << " to " // << entry.broadcast().toString().toStdString() << ":" << BroadcastPort; } } } } } void DeviceListModel::stopSearchDevice() { if (m_timerId >= 0) { killTimer(m_timerId); m_timerId = -1; } emit isSearchingChanged(); } void DeviceListModel::timerEvent(QTimerEvent *event) { broadcast(); m_retries++; emit searchProgressChanged(); if (m_retries >= RetryCount) { QTimer::singleShot(0, this, &DeviceListModel::stopSearchDevice); } }