AntiClipSettings/DeviceListModel.cpp
2024-08-21 16:03:49 +08:00

220 lines
8.7 KiB
C++

#include "DeviceListModel.h"
#include "BoostLog.h"
#include "DeviceConnection.h"
#include <QNetworkDatagram>
#include <QNetworkInterface>
#include <QTimer>
#include <QUdpSocket>
#include <boost/json/parse.hpp>
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<int, QByteArray> DeviceListModel::roleNames() const {
QHash<int, QByteArray> 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<DeviceConnection> DeviceListModel::device(int index) {
std::shared_ptr<DeviceConnection> 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;
}
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<QNetworkAddressEntry> 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<float>(m_retries) * 100 / RetryCount;
}
void DeviceListModel::onDeviceConnected() {
auto device = dynamic_cast<DeviceConnection *>(sender());
auto iterator =
std::find_if(m_devices.cbegin(), m_devices.cend(),
[device](const std::shared_ptr<DeviceConnection> &item) { return item.get() == device; });
if (iterator != m_devices.cend()) {
QList<int> roles;
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<DeviceConnection *>(sender());
auto iterator =
std::find_if(m_devices.cbegin(), m_devices.cend(),
[device](const std::shared_ptr<DeviceConnection> &item) { return item.get() == device; });
if (iterator != m_devices.cend()) {
int row = std::distance(m_devices.cbegin(), iterator);
QList<int> roles;
roles << OnlineStatusRole;
emit dataChanged(index(row), index(row), roles);
LOG(info) << "device " << row << " disconnected.";
}
}
void DeviceListModel::onDeviceReplyReadyRead() {
auto udp = dynamic_cast<QUdpSocket *>(sender());
while (udp->hasPendingDatagrams()) {
QNetworkDatagram datagram = udp->receiveDatagram();
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()));
}
device.ip = datagram.senderAddress().toString();
auto iterator = std::find_if(m_devices.cbegin(), m_devices.cend(),
[&device](const std::shared_ptr<DeviceConnection> &item) {
return item->infomation().deviceId == device.deviceId;
});
if (iterator == m_devices.cend()) {
auto connection = std::shared_ptr<DeviceConnection>(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();
}
LOG(info) << "Received datagram from " << datagram.senderAddress().toString().toStdString() << ":"
<< datagram.senderPort() << ", Data: " << datagram.data().toStdString();
}
}
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<QNetworkAddressEntry> 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;
}
if (!m_udpSockets.empty()) {
for (auto &socket : m_udpSockets) {
socket->deleteLater();
}
m_udpSockets.clear();
}
emit isSearchingChanged();
}
void DeviceListModel::timerEvent(QTimerEvent *event) {
broadcast();
m_retries++;
emit searchProgressChanged();
if (m_retries >= RetryCount) {
QTimer::singleShot(0, this, &DeviceListModel::stopSearchDevice);
}
}